├── .editorconfig
├── .gitignore
├── README.md
├── app
├── app.html
├── app.js
├── assets
│ ├── baddie.png
│ ├── diamond.png
│ ├── dude.png
│ ├── firstaid.png
│ ├── platform.png
│ ├── sky.png
│ └── star.png
├── background.js
├── env.js
├── hello_world
│ ├── boilerplate.spec.js
│ ├── hello_universe.js
│ ├── hello_universe.spec.js
│ ├── hello_world.js
│ └── hello_world.spec.js
├── helpers
│ ├── context_menu.js
│ ├── dev_menu_template.js
│ ├── edit_menu_template.js
│ ├── external_links.js
│ └── window.js
├── libs
│ └── phaser.min.js
├── package.json
└── stylesheets
│ └── main.less
├── config
├── env_development.json
├── env_production.json
└── env_test.json
├── gulpfile.js
├── package.json
├── resources
├── icon.png
├── linux
│ ├── DEBIAN
│ │ └── control
│ └── app.desktop
├── osx
│ ├── Info.plist
│ ├── appdmg.json
│ ├── child.plist
│ ├── dmg-background.png
│ ├── dmg-background@2x.png
│ ├── dmg-icon.icns
│ ├── helper_apps
│ │ ├── Info EH.plist
│ │ ├── Info NP.plist
│ │ └── Info.plist
│ ├── icon.icns
│ └── parent.plist
└── windows
│ ├── icon.ico
│ ├── installer.nsi
│ ├── setup-banner.bmp
│ └── setup-icon.ico
└── tasks
├── build
├── build.js
├── bundle.js
└── generate_spec_imports.js
├── install_native_module.js
├── rebuild_native_modules.js
├── release
├── linux.js
├── osx.js
├── release.js
└── windows.js
├── start.js
└── utils.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 4
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.json]
13 | indent_size = 2
14 |
15 | [*.md]
16 | trim_trailing_whitespace = false
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.log
3 | .DS_Store
4 | Thumbs.db
5 | *.autogenerated
6 | /build/
7 | /releases/
8 | /tmp/
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Electron boilerplate + Phaser tutorial
2 |
3 | This repository is a merge of two different repositories :
4 |
5 | - [electron-boilerplate](https://github.com/szwacz/electron-boilerplate.git)
6 | - Phaser html5 game library [making your first game tutorial](http://phaser.io/tutorials/making-your-first-phaser-game)
7 |
8 | ```
9 | git clone https://github.com/Fax/electron-phaser
10 | cd electron-phaser
11 | npm install
12 | npm start
13 | ```
14 |
15 | # Usages
16 |
17 | Follow the README on the repository https://github.com/szwacz/electron-boilerplate.git.
18 |
19 | # License
20 |
21 | MIT License
22 |
--------------------------------------------------------------------------------
/app/app.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Electron Boilerplate
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/app.js:
--------------------------------------------------------------------------------
1 | // Here is the starting point for your application code.
2 | // All stuff below is just to show you how it works. You can delete all of it.
3 |
4 | // Use new ES6 modules syntax for everything.
5 | import os from 'os'; // native node.js module
6 | import { remote } from 'electron'; // native electron module
7 | import jetpack from 'fs-jetpack'; // module loaded from npm
8 | import { greet } from './hello_world/hello_world'; // code authored by you in this project
9 | import env from './env';
10 |
11 |
12 | console.log('Loaded environment variables:', env);
13 |
14 | var app = remote.app;
15 | var appDir = jetpack.cwd(app.getAppPath());
16 |
17 | // Holy crap! This is browser window with HTML and stuff, but I can read
18 | // here files like it is node.js! Welcome to Electron world :)
19 | console.log('The author of this app is:', appDir.read('package.json', 'json').author);
20 |
21 | document.addEventListener('DOMContentLoaded', function () {
22 |
23 | var game = new Phaser.Game(800, 600, Phaser.AUTO, 'main', { preload: preload, create: create, update: update });
24 |
25 | function preload() {
26 |
27 | game.load.image('sky', 'assets/sky.png');
28 | game.load.image('ground', 'assets/platform.png');
29 | game.load.image('star', 'assets/star.png');
30 | game.load.spritesheet('dude', 'assets/dude.png', 32, 48);
31 |
32 | }
33 |
34 | var player;
35 | var platforms;
36 | var cursors;
37 |
38 | var stars;
39 | var score = 0;
40 | var scoreText;
41 |
42 | function create() {
43 |
44 | // We're going to be using physics, so enable the Arcade Physics system
45 | game.physics.startSystem(Phaser.Physics.ARCADE);
46 |
47 | // A simple background for our game
48 | game.add.sprite(0, 0, 'sky');
49 |
50 | // The platforms group contains the ground and the 2 ledges we can jump on
51 | platforms = game.add.group();
52 |
53 | // We will enable physics for any object that is created in this group
54 | platforms.enableBody = true;
55 |
56 | // Here we create the ground.
57 | var ground = platforms.create(0, game.world.height - 64, 'ground');
58 |
59 | // Scale it to fit the width of the game (the original sprite is 400x32 in size)
60 | ground.scale.setTo(2, 2);
61 |
62 | // This stops it from falling away when you jump on it
63 | ground.body.immovable = true;
64 |
65 | // Now let's create two ledges
66 | var ledge = platforms.create(400, 400, 'ground');
67 | ledge.body.immovable = true;
68 |
69 | ledge = platforms.create(-150, 250, 'ground');
70 | ledge.body.immovable = true;
71 |
72 | // The player and its settings
73 | player = game.add.sprite(32, game.world.height - 150, 'dude');
74 |
75 | // We need to enable physics on the player
76 | game.physics.arcade.enable(player);
77 |
78 | // Player physics properties. Give the little guy a slight bounce.
79 | player.body.bounce.y = 0.2;
80 | player.body.gravity.y = 300;
81 | player.body.collideWorldBounds = true;
82 |
83 | // Our two animations, walking left and right.
84 | player.animations.add('left', [0, 1, 2, 3], 10, true);
85 | player.animations.add('right', [5, 6, 7, 8], 10, true);
86 |
87 | // Finally some stars to collect
88 | stars = game.add.group();
89 |
90 | // We will enable physics for any star that is created in this group
91 | stars.enableBody = true;
92 |
93 | // Here we'll create 12 of them evenly spaced apart
94 | for (var i = 0; i < 12; i++)
95 | {
96 | // Create a star inside of the 'stars' group
97 | var star = stars.create(i * 70, 0, 'star');
98 |
99 | // Let gravity do its thing
100 | star.body.gravity.y = 300;
101 |
102 | // This just gives each star a slightly random bounce value
103 | star.body.bounce.y = 0.7 + Math.random() * 0.2;
104 | }
105 |
106 | // The score
107 | scoreText = game.add.text(16, 16, 'score: 0', { fontSize: '32px', fill: '#000' });
108 |
109 | // Our controls.
110 | cursors = game.input.keyboard.createCursorKeys();
111 |
112 | }
113 |
114 | function update() {
115 |
116 | // Collide the player and the stars with the platforms
117 | game.physics.arcade.collide(player, platforms);
118 | game.physics.arcade.collide(stars, platforms);
119 |
120 | // Checks to see if the player overlaps with any of the stars, if he does call the collectStar function
121 | game.physics.arcade.overlap(player, stars, collectStar, null, this);
122 |
123 | // Reset the players velocity (movement)
124 | player.body.velocity.x = 0;
125 |
126 | if (cursors.left.isDown)
127 | {
128 | // Move to the left
129 | player.body.velocity.x = -150;
130 |
131 | player.animations.play('left');
132 | }
133 | else if (cursors.right.isDown)
134 | {
135 | // Move to the right
136 | player.body.velocity.x = 150;
137 |
138 | player.animations.play('right');
139 | }
140 | else
141 | {
142 | // Stand still
143 | player.animations.stop();
144 |
145 | player.frame = 4;
146 | }
147 |
148 | // Allow the player to jump if they are touching the ground.
149 | if (cursors.up.isDown && player.body.touching.down)
150 | {
151 | player.body.velocity.y = -350;
152 | }
153 |
154 | }
155 |
156 | function collectStar (player, star) {
157 |
158 | // Removes the star from the screen
159 | star.kill();
160 |
161 | // Add and update the score
162 | score += 10;
163 | scoreText.text = 'Score: ' + score;
164 |
165 | }
166 | });
167 |
--------------------------------------------------------------------------------
/app/assets/baddie.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fax/electron-phaser/8b1fa0dd1c8bdc72c1b30f787dcf46be2e2143d6/app/assets/baddie.png
--------------------------------------------------------------------------------
/app/assets/diamond.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fax/electron-phaser/8b1fa0dd1c8bdc72c1b30f787dcf46be2e2143d6/app/assets/diamond.png
--------------------------------------------------------------------------------
/app/assets/dude.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fax/electron-phaser/8b1fa0dd1c8bdc72c1b30f787dcf46be2e2143d6/app/assets/dude.png
--------------------------------------------------------------------------------
/app/assets/firstaid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fax/electron-phaser/8b1fa0dd1c8bdc72c1b30f787dcf46be2e2143d6/app/assets/firstaid.png
--------------------------------------------------------------------------------
/app/assets/platform.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fax/electron-phaser/8b1fa0dd1c8bdc72c1b30f787dcf46be2e2143d6/app/assets/platform.png
--------------------------------------------------------------------------------
/app/assets/sky.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fax/electron-phaser/8b1fa0dd1c8bdc72c1b30f787dcf46be2e2143d6/app/assets/sky.png
--------------------------------------------------------------------------------
/app/assets/star.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fax/electron-phaser/8b1fa0dd1c8bdc72c1b30f787dcf46be2e2143d6/app/assets/star.png
--------------------------------------------------------------------------------
/app/background.js:
--------------------------------------------------------------------------------
1 | // This is main process of Electron, started as first thing when your
2 | // app starts. This script is running through entire life of your application.
3 | // It doesn't have any windows which you can see on screen, but we can open
4 | // window from here.
5 |
6 | import { app, Menu } from 'electron';
7 | import { devMenuTemplate } from './helpers/dev_menu_template';
8 | import { editMenuTemplate } from './helpers/edit_menu_template';
9 | import createWindow from './helpers/window';
10 |
11 | // Special module holding environment variables which you declared
12 | // in config/env_xxx.json file.
13 | import env from './env';
14 |
15 | var mainWindow;
16 |
17 | var setApplicationMenu = function () {
18 | var menus = [editMenuTemplate];
19 | if (env.name !== 'production') {
20 | menus.push(devMenuTemplate);
21 | }
22 | Menu.setApplicationMenu(Menu.buildFromTemplate(menus));
23 | };
24 |
25 | app.on('ready', function () {
26 | setApplicationMenu();
27 |
28 | var mainWindow = createWindow('main', {
29 | width: 1000,
30 | height: 600
31 | });
32 |
33 | mainWindow.loadURL('file://' + __dirname + '/app.html');
34 |
35 | if (env.name !== 'production') {
36 | //mainWindow.openDevTools();
37 | }
38 | });
39 |
40 | app.on('window-all-closed', function () {
41 | app.quit();
42 | });
43 |
--------------------------------------------------------------------------------
/app/env.js:
--------------------------------------------------------------------------------
1 | // Simple wrapper exposing environment variables to rest of the code.
2 |
3 | import jetpack from 'fs-jetpack';
4 |
5 | // The variables have been written to `env.json` by the build process.
6 | var env = jetpack.cwd(__dirname).read('env.json', 'json');
7 |
8 | export default env;
9 |
--------------------------------------------------------------------------------
/app/hello_world/boilerplate.spec.js:
--------------------------------------------------------------------------------
1 | // Tests here are for easier maintenance of this boilerplate.
2 | // Feel free to delete this file in your own project.
3 |
4 | import { expect } from 'chai';
5 | import env from '../env';
6 |
7 | describe("boilerplate tests", function () {
8 |
9 | it("environment variables should be on their place", function () {
10 | expect(env.name).to.equal('test');
11 | expect(env.description).to.equal('Add here any environment specific stuff you like.');
12 | });
13 |
14 | });
15 |
--------------------------------------------------------------------------------
/app/hello_world/hello_universe.js:
--------------------------------------------------------------------------------
1 | var greet = function () {
2 | return 'Hello Universe!';
3 | };
4 |
5 | export default greet;
6 |
--------------------------------------------------------------------------------
/app/hello_world/hello_universe.spec.js:
--------------------------------------------------------------------------------
1 | import { expect } from 'chai';
2 | import greet from './hello_universe';
3 |
4 | describe("hello universe", function () {
5 |
6 | it("greets better than hello world", function () {
7 | expect(greet()).to.equal('Hello Universe!');
8 | });
9 |
10 | });
11 |
--------------------------------------------------------------------------------
/app/hello_world/hello_world.js:
--------------------------------------------------------------------------------
1 | export var greet = function () {
2 | return 'Hello World!';
3 | };
4 |
5 | export var bye = function () {
6 | return 'See ya!';
7 | };
8 |
--------------------------------------------------------------------------------
/app/hello_world/hello_world.spec.js:
--------------------------------------------------------------------------------
1 | import { expect } from 'chai';
2 | import { greet, bye } from './hello_world';
3 |
4 | describe("hello world", function () {
5 |
6 | it("greets", function () {
7 | expect(greet()).to.equal('Hello World!');
8 | });
9 |
10 | it("says goodbye", function () {
11 | expect(bye()).to.equal('See ya!');
12 | });
13 |
14 | });
15 |
--------------------------------------------------------------------------------
/app/helpers/context_menu.js:
--------------------------------------------------------------------------------
1 | // This gives you default context menu (cut, copy, paste)
2 | // in all input fields and textareas across your app.
3 |
4 | (function () {
5 | 'use strict';
6 |
7 | var remote = require('electron').remote;
8 | var Menu = remote.Menu;
9 | var MenuItem = remote.MenuItem;
10 |
11 | var isAnyTextSelected = function () {
12 | return window.getSelection().toString() !== '';
13 | };
14 |
15 | var cut = new MenuItem({
16 | label: "Cut",
17 | click: function () {
18 | document.execCommand("cut");
19 | }
20 | });
21 |
22 | var copy = new MenuItem({
23 | label: "Copy",
24 | click: function () {
25 | document.execCommand("copy");
26 | }
27 | });
28 |
29 | var paste = new MenuItem({
30 | label: "Paste",
31 | click: function () {
32 | document.execCommand("paste");
33 | }
34 | });
35 |
36 | var normalMenu = new Menu();
37 | normalMenu.append(copy);
38 |
39 | var textEditingMenu = new Menu();
40 | textEditingMenu.append(cut);
41 | textEditingMenu.append(copy);
42 | textEditingMenu.append(paste);
43 |
44 | document.addEventListener('contextmenu', function (e) {
45 | switch (e.target.nodeName) {
46 | case 'TEXTAREA':
47 | case 'INPUT':
48 | e.preventDefault();
49 | textEditingMenu.popup(remote.getCurrentWindow());
50 | break;
51 | default:
52 | if (isAnyTextSelected()) {
53 | e.preventDefault();
54 | normalMenu.popup(remote.getCurrentWindow());
55 | }
56 | }
57 | }, false);
58 |
59 | }());
60 |
--------------------------------------------------------------------------------
/app/helpers/dev_menu_template.js:
--------------------------------------------------------------------------------
1 | import { app, BrowserWindow } from 'electron';
2 |
3 | export var devMenuTemplate = {
4 | label: 'Development',
5 | submenu: [{
6 | label: 'Reload',
7 | accelerator: 'CmdOrCtrl+R',
8 | click: function () {
9 | BrowserWindow.getFocusedWindow().webContents.reloadIgnoringCache();
10 | }
11 | },{
12 | label: 'Toggle DevTools',
13 | accelerator: 'Alt+CmdOrCtrl+I',
14 | click: function () {
15 | BrowserWindow.getFocusedWindow().toggleDevTools();
16 | }
17 | },{
18 | label: 'Quit',
19 | accelerator: 'CmdOrCtrl+Q',
20 | click: function () {
21 | app.quit();
22 | }
23 | }]
24 | };
25 |
--------------------------------------------------------------------------------
/app/helpers/edit_menu_template.js:
--------------------------------------------------------------------------------
1 | export var editMenuTemplate = {
2 | label: 'Edit',
3 | submenu: [
4 | { label: "Undo", accelerator: "CmdOrCtrl+Z", selector: "undo:" },
5 | { label: "Redo", accelerator: "Shift+CmdOrCtrl+Z", selector: "redo:" },
6 | { type: "separator" },
7 | { label: "Cut", accelerator: "CmdOrCtrl+X", selector: "cut:" },
8 | { label: "Copy", accelerator: "CmdOrCtrl+C", selector: "copy:" },
9 | { label: "Paste", accelerator: "CmdOrCtrl+V", selector: "paste:" },
10 | { label: "Select All", accelerator: "CmdOrCtrl+A", selector: "selectAll:" }
11 | ]
12 | };
13 |
--------------------------------------------------------------------------------
/app/helpers/external_links.js:
--------------------------------------------------------------------------------
1 | // Convenient way for opening links in external browser, not in the app.
2 | // Useful especially if you have a lot of links to deal with.
3 | //
4 | // Usage:
5 | //
6 | // Every link with class ".js-external-link" will be opened in external browser.
7 | // google
8 | //
9 | // The same behaviour for many links can be achieved by adding
10 | // this class to any parent tag of an anchor tag.
11 | //
12 | // google
13 | // bing
14 | //
15 |
16 | (function () {
17 | 'use strict';
18 |
19 | var shell = require('electron').shell;
20 |
21 | var supportExternalLinks = function (e) {
22 | var href;
23 | var isExternal = false;
24 |
25 | var checkDomElement = function (element) {
26 | if (element.nodeName === 'A') {
27 | href = element.getAttribute('href');
28 | }
29 | if (element.classList.contains('js-external-link')) {
30 | isExternal = true;
31 | }
32 | if (href && isExternal) {
33 | shell.openExternal(href);
34 | e.preventDefault();
35 | } else if (element.parentElement) {
36 | checkDomElement(element.parentElement);
37 | }
38 | };
39 |
40 | checkDomElement(e.target);
41 | };
42 |
43 | document.addEventListener('click', supportExternalLinks, false);
44 | }());
45 |
--------------------------------------------------------------------------------
/app/helpers/window.js:
--------------------------------------------------------------------------------
1 | // This helper remembers the size and position of your windows (and restores
2 | // them in that place after app relaunch).
3 | // Can be used for more than one window, just construct many
4 | // instances of it and give each different name.
5 |
6 | import { app, BrowserWindow, screen } from 'electron';
7 | import jetpack from 'fs-jetpack';
8 |
9 | export default function (name, options) {
10 |
11 | var userDataDir = jetpack.cwd(app.getPath('userData'));
12 | var stateStoreFile = 'window-state-' + name +'.json';
13 | var defaultSize = {
14 | width: options.width,
15 | height: options.height
16 | };
17 | var state = {};
18 | var win;
19 |
20 | var restore = function () {
21 | var restoredState = {};
22 | try {
23 | restoredState = userDataDir.read(stateStoreFile, 'json');
24 | } catch (err) {
25 | // For some reason json can't be read (might be corrupted).
26 | // No worries, we have defaults.
27 | }
28 | return Object.assign({}, defaultSize, restoredState);
29 | };
30 |
31 | var getCurrentPosition = function () {
32 | var position = win.getPosition();
33 | var size = win.getSize();
34 | return {
35 | x: position[0],
36 | y: position[1],
37 | width: size[0],
38 | height: size[1]
39 | };
40 | };
41 |
42 | var windowWithinBounds = function (windowState, bounds) {
43 | return windowState.x >= bounds.x &&
44 | windowState.y >= bounds.y &&
45 | windowState.x + windowState.width <= bounds.x + bounds.width &&
46 | windowState.y + windowState.height <= bounds.y + bounds.height;
47 | };
48 |
49 | var resetToDefaults = function (windowState) {
50 | var bounds = screen.getPrimaryDisplay().bounds;
51 | return Object.assign({}, defaultSize, {
52 | x: (bounds.width - defaultSize.width) / 2,
53 | y: (bounds.height - defaultSize.height) / 2
54 | });
55 | };
56 |
57 | var ensureVisibleOnSomeDisplay = function (windowState) {
58 | var visible = screen.getAllDisplays().some(function (display) {
59 | return windowWithinBounds(windowState, display.bounds);
60 | });
61 | if (!visible) {
62 | // Window is partially or fully not visible now.
63 | // Reset it to safe defaults.
64 | return resetToDefaults(windowState);
65 | }
66 | return windowState;
67 | };
68 |
69 | var saveState = function () {
70 | if (!win.isMinimized() && !win.isMaximized()) {
71 | Object.assign(state, getCurrentPosition());
72 | }
73 | userDataDir.write(stateStoreFile, state, { atomic: true });
74 | };
75 |
76 | state = ensureVisibleOnSomeDisplay(restore());
77 |
78 | win = new BrowserWindow(Object.assign({}, options, state));
79 |
80 | win.on('close', saveState);
81 |
82 | return win;
83 | }
84 |
--------------------------------------------------------------------------------
/app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "electron-phaser",
3 | "productName": "Electron Phaser Boilerplate",
4 | "description": "The first tutorial taken from the phaser website and embedded in electron",
5 | "version": "0.1.0",
6 | "author": "Fax",
7 | "copyright": "© 2016, Fabrizio Rapelli",
8 | "main": "background.js",
9 | "dependencies": {
10 | "fs-jetpack": "^0.9.0"
11 | },
12 | "packageNameTemplate": "{{name}}-v{{version}}-{{platform}}-{{arch}}",
13 | "osx": {
14 | "build": "1",
15 | "identifier": "com.example.electron-boilerplate",
16 | "LSApplicationCategoryType": "public.app-category.productivity",
17 | "//codeSignIdentitiy": {
18 | "dmg": "Developer ID Application: Company Name (APPIDENTITY)",
19 | "MAS": "3rd Party Mac Developer Application: Company Name (APPIDENTITY)",
20 | "MASInstaller": "3rd Party Mac Developer Installer: Company Name (APPIDENTITY)"
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/stylesheets/main.less:
--------------------------------------------------------------------------------
1 | html, body {
2 | width: 100%;
3 | height: 100%;
4 | margin: 0;
5 | padding: 0;
6 | }
7 |
8 | body {
9 | display: flex;
10 | justify-content: center;
11 | align-items: center;
12 | font-family: sans-serif;
13 | }
14 |
15 | a {
16 | text-decoration: none;
17 | }
18 |
19 | .container {
20 | text-align: center;
21 | }
22 |
23 | .subtitle {
24 | color: gray;
25 | }
26 |
--------------------------------------------------------------------------------
/config/env_development.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "development",
3 | "description": "Add here any environment specific stuff you like."
4 | }
5 |
--------------------------------------------------------------------------------
/config/env_production.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "production",
3 | "description": "Add here any environment specific stuff you like."
4 | }
5 |
--------------------------------------------------------------------------------
/config/env_test.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "test",
3 | "description": "Add here any environment specific stuff you like."
4 | }
5 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | require('./tasks/build/build');
4 | require('./tasks/release/release');
5 | require('./tasks/start');
6 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "devDependencies": {
3 | "asar": "^0.11.0",
4 | "chai": "^3.5.0",
5 | "electron-mocha": "^2.0.0",
6 | "electron-prebuilt": "^1.0.1",
7 | "fs-jetpack": "^0.9.0",
8 | "gulp": "^3.9.1",
9 | "gulp-batch": "^1.0.5",
10 | "gulp-less": "^3.0.3",
11 | "gulp-plumber": "^1.1.0",
12 | "gulp-util": "^3.0.6",
13 | "gulp-watch": "^4.3.5",
14 | "q": "^1.4.1",
15 | "rollup": "^0.26.3",
16 | "yargs": "^4.2.0"
17 | },
18 | "optionalDependencies": {
19 | "appdmg": "^0.3.2",
20 | "rcedit": "^0.5.0"
21 | },
22 | "scripts": {
23 | "postinstall": "cd app && npm install",
24 | "build": "gulp build",
25 | "release": "gulp release --env=production",
26 | "start": "gulp start",
27 | "pretest": "gulp build --env=test",
28 | "test": "electron-mocha build --renderer",
29 | "install-native": "node ./tasks/install_native_module"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/resources/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fax/electron-phaser/8b1fa0dd1c8bdc72c1b30f787dcf46be2e2143d6/resources/icon.png
--------------------------------------------------------------------------------
/resources/linux/DEBIAN/control:
--------------------------------------------------------------------------------
1 | Package: {{name}}
2 | Version: {{version}}
3 | Maintainer: {{author}}
4 | Priority: optional
5 | Architecture: amd64
6 | Installed-Size: {{size}}
7 | Description: {{description}}
8 |
--------------------------------------------------------------------------------
/resources/linux/app.desktop:
--------------------------------------------------------------------------------
1 | [Desktop Entry]
2 | Version=1.0
3 | Type=Application
4 | Encoding=UTF-8
5 | Name={{productName}}
6 | Comment={{description}}
7 | Exec=/opt/{{name}}/{{name}}
8 | Path=/opt/{{name}}/
9 | Icon=/opt/{{name}}/icon.png
10 | Terminal=false
11 | Categories=Application;
12 |
--------------------------------------------------------------------------------
/resources/osx/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDisplayName
6 | {{productName}}
7 | CFBundleExecutable
8 | {{productName}}
9 | CFBundleIconFile
10 | icon.icns
11 | CFBundleIdentifier
12 | {{identifier}}
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | {{productName}}
17 | CFBundlePackageType
18 | APPL
19 | CFBundleVersion
20 | {{build}}
21 | CFBundleVersionString
22 | {{build}}
23 | CFBundleShortVersionString
24 | {{version}}
25 | CFBundleGetInfoString
26 | {{version}}
27 | LSMinimumSystemVersion
28 | 10.8.0
29 | NSMainNibFile
30 | MainMenu
31 | NSPrincipalClass
32 | AtomApplication
33 | NSSupportsAutomaticGraphicsSwitching
34 |
35 | NSHumanReadableCopyright
36 | {{copyright}}
37 | LSApplicationCategoryType
38 | {{LSApplicationCategoryType}}
39 |
40 |
41 |
--------------------------------------------------------------------------------
/resources/osx/appdmg.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "{{productName}}",
3 | "icon": "{{dmgIcon}}",
4 | "background": "{{dmgBackground}}",
5 | "icon-size": 128,
6 | "contents": [
7 | { "x": 410, "y": 220, "type": "link", "path": "/Applications" },
8 | { "x": 130, "y": 220, "type": "file", "path": "{{appPath}}" }
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/resources/osx/child.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.inherit
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/resources/osx/dmg-background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fax/electron-phaser/8b1fa0dd1c8bdc72c1b30f787dcf46be2e2143d6/resources/osx/dmg-background.png
--------------------------------------------------------------------------------
/resources/osx/dmg-background@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fax/electron-phaser/8b1fa0dd1c8bdc72c1b30f787dcf46be2e2143d6/resources/osx/dmg-background@2x.png
--------------------------------------------------------------------------------
/resources/osx/dmg-icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fax/electron-phaser/8b1fa0dd1c8bdc72c1b30f787dcf46be2e2143d6/resources/osx/dmg-icon.icns
--------------------------------------------------------------------------------
/resources/osx/helper_apps/Info EH.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDisplayName
6 | {{productName}} Helper EH
7 | CFBundleExecutable
8 | {{productName}} Helper EH
9 | CFBundleIdentifier
10 | {{identifier}}.helper.EH
11 | CFBundleName
12 | {{productName}} Helper EH
13 | CFBundlePackageType
14 | APPL
15 | DTSDKName
16 | macosx
17 | LSUIElement
18 |
19 | NSSupportsAutomaticGraphicsSwitching
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/resources/osx/helper_apps/Info NP.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDisplayName
6 | {{productName}} Helper NP
7 | CFBundleExecutable
8 | {{productName}} Helper NP
9 | CFBundleIdentifier
10 | {{identifier}}.helper.NP
11 | CFBundleName
12 | {{productName}} Helper NP
13 | CFBundlePackageType
14 | APPL
15 | DTSDKName
16 | macosx
17 | LSUIElement
18 |
19 | NSSupportsAutomaticGraphicsSwitching
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/resources/osx/helper_apps/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleIdentifier
6 | {{identifier}}.helper
7 | CFBundleName
8 | {{productName}} Helper
9 | CFBundlePackageType
10 | APPL
11 | DTSDKName
12 | macosx
13 | LSUIElement
14 |
15 | NSSupportsAutomaticGraphicsSwitching
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/resources/osx/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fax/electron-phaser/8b1fa0dd1c8bdc72c1b30f787dcf46be2e2143d6/resources/osx/icon.icns
--------------------------------------------------------------------------------
/resources/osx/parent.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.device.camera
8 |
9 | com.apple.security.device.microphone
10 |
11 | com.apple.security.files.user-selected.read-only
12 |
13 | com.apple.security.files.user-selected.read-write
14 |
15 | com.apple.security.network.client
16 |
17 | com.apple.security.network.server
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/resources/windows/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fax/electron-phaser/8b1fa0dd1c8bdc72c1b30f787dcf46be2e2143d6/resources/windows/icon.ico
--------------------------------------------------------------------------------
/resources/windows/installer.nsi:
--------------------------------------------------------------------------------
1 | ; NSIS packaging/install script
2 | ; Docs: http://nsis.sourceforge.net/Docs/Contents.html
3 |
4 | !include LogicLib.nsh
5 | !include nsDialogs.nsh
6 |
7 | ; --------------------------------
8 | ; Variables
9 | ; --------------------------------
10 |
11 | !define dest "{{dest}}"
12 | !define src "{{src}}"
13 | !define name "{{name}}"
14 | !define productName "{{productName}}"
15 | !define author "{{author}}"
16 | !define version "{{version}}"
17 | !define icon "{{icon}}"
18 | !define setupIcon "{{setupIcon}}"
19 | !define banner "{{banner}}"
20 |
21 | !define exec "{{productName}}.exe"
22 |
23 | !define regkey "Software\${productName}"
24 | !define uninstkey "Software\Microsoft\Windows\CurrentVersion\Uninstall\${productName}"
25 |
26 | !define uninstaller "uninstall.exe"
27 |
28 | ; --------------------------------
29 | ; Installation
30 | ; --------------------------------
31 |
32 | Unicode true
33 | SetCompressor /SOLID lzma
34 |
35 | Name "${productName}"
36 | Icon "${setupIcon}"
37 | OutFile "${dest}"
38 | InstallDir "$PROGRAMFILES\${productName}"
39 | InstallDirRegKey HKLM "${regkey}" ""
40 |
41 | RequestExecutionLevel admin
42 | CRCCheck on
43 | SilentInstall normal
44 |
45 | XPStyle on
46 | ShowInstDetails nevershow
47 | AutoCloseWindow false
48 | WindowIcon off
49 |
50 | Caption "${productName} Setup"
51 | ; Don't add sub-captions to title bar
52 | SubCaption 3 " "
53 | SubCaption 4 " "
54 |
55 | Page custom welcome
56 | Page instfiles
57 |
58 | Var Image
59 | Var ImageHandle
60 |
61 | Function .onInit
62 |
63 | ; Extract banner image for welcome page
64 | InitPluginsDir
65 | ReserveFile "${banner}"
66 | File /oname=$PLUGINSDIR\banner.bmp "${banner}"
67 |
68 | FunctionEnd
69 |
70 | ; Custom welcome page
71 | Function welcome
72 |
73 | nsDialogs::Create 1018
74 |
75 | ${NSD_CreateLabel} 185 1u 210 100% "Welcome to ${productName} version ${version} installer.$\r$\n$\r$\nClick install to begin."
76 |
77 | ${NSD_CreateBitmap} 0 0 170 210 ""
78 | Pop $Image
79 | ${NSD_SetImage} $Image $PLUGINSDIR\banner.bmp $ImageHandle
80 |
81 | nsDialogs::Show
82 |
83 | ${NSD_FreeImage} $ImageHandle
84 |
85 | FunctionEnd
86 |
87 | ; Installation declarations
88 | Section "Install"
89 |
90 | WriteRegStr HKLM "${regkey}" "Install_Dir" "$INSTDIR"
91 | WriteRegStr HKLM "${uninstkey}" "DisplayName" "${productName}"
92 | WriteRegStr HKLM "${uninstkey}" "DisplayIcon" '"$INSTDIR\icon.ico"'
93 | WriteRegStr HKLM "${uninstkey}" "UninstallString" '"$INSTDIR\${uninstaller}"'
94 | WriteRegStr HKLM "${uninstkey}" "Publisher" "${author}"
95 | WriteRegStr HKLM "${uninstkey}" "DisplayVersion" "${version}"
96 |
97 | ; Remove all application files copied by previous installation
98 | RMDir /r "$INSTDIR"
99 |
100 | SetOutPath $INSTDIR
101 |
102 | ; Include all files from /build directory
103 | File /r "${src}\*"
104 |
105 | ; Create start menu shortcut
106 | SetShellVarContext all
107 | CreateShortCut "$SMPROGRAMS\${productName}.lnk" "$INSTDIR\${exec}" "" "$INSTDIR\icon.ico"
108 | ; Create desktop shortcut
109 | CreateShortCut "$DESKTOP\${productName}.lnk" "$INSTDIR\${exec}" "" "$INSTDIR\icon.ico"
110 |
111 | WriteUninstaller "${uninstaller}"
112 |
113 | SectionEnd
114 |
115 | ; --------------------------------
116 | ; Uninstaller
117 | ; --------------------------------
118 |
119 | ShowUninstDetails nevershow
120 |
121 | UninstallCaption "Uninstall ${productName}"
122 | UninstallText "Don't like ${productName} anymore? Hit uninstall button."
123 | UninstallIcon "${icon}"
124 |
125 | UninstPage custom un.confirm un.confirmOnLeave
126 | UninstPage instfiles
127 |
128 | Var RemoveAppDataCheckbox
129 | Var RemoveAppDataCheckbox_State
130 |
131 | ; Custom uninstall confirm page
132 | Function un.confirm
133 |
134 | nsDialogs::Create 1018
135 |
136 | ${NSD_CreateLabel} 1u 1u 100% 24u "If you really want to remove ${productName} from your computer press uninstall button."
137 |
138 | ${NSD_CreateCheckbox} 1u 35u 100% 10u "Remove also my ${productName} personal data"
139 | Pop $RemoveAppDataCheckbox
140 |
141 | nsDialogs::Show
142 |
143 | FunctionEnd
144 |
145 | Function un.confirmOnLeave
146 |
147 | ; Save checkbox state on page leave
148 | ${NSD_GetState} $RemoveAppDataCheckbox $RemoveAppDataCheckbox_State
149 |
150 | FunctionEnd
151 |
152 | ; Uninstall declarations
153 | Section "Uninstall"
154 |
155 | DeleteRegKey HKLM "${uninstkey}"
156 | DeleteRegKey HKLM "${regkey}"
157 |
158 | SetShellVarContext all
159 | Delete "$SMPROGRAMS\${productName}.lnk"
160 | ; Remove desktop shortcut
161 | Delete "$DESKTOP\${productName}.lnk"
162 | ; Remove whole directory from Program Files
163 | RMDir /r "$INSTDIR"
164 |
165 | ; Remove also appData directory generated by your app if user checked this option
166 | ${If} $RemoveAppDataCheckbox_State == ${BST_CHECKED}
167 | RMDir /r "$APPDATA\${productName}"
168 | ${EndIf}
169 |
170 | SectionEnd
171 |
--------------------------------------------------------------------------------
/resources/windows/setup-banner.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fax/electron-phaser/8b1fa0dd1c8bdc72c1b30f787dcf46be2e2143d6/resources/windows/setup-banner.bmp
--------------------------------------------------------------------------------
/resources/windows/setup-icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Fax/electron-phaser/8b1fa0dd1c8bdc72c1b30f787dcf46be2e2143d6/resources/windows/setup-icon.ico
--------------------------------------------------------------------------------
/tasks/build/build.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var pathUtil = require('path');
4 | var Q = require('q');
5 | var gulp = require('gulp');
6 | var less = require('gulp-less');
7 | var watch = require('gulp-watch');
8 | var batch = require('gulp-batch');
9 | var plumber = require('gulp-plumber');
10 | var jetpack = require('fs-jetpack');
11 |
12 | var bundle = require('./bundle');
13 | var generateSpecImportsFile = require('./generate_spec_imports');
14 | var utils = require('../utils');
15 |
16 | var projectDir = jetpack;
17 | var srcDir = projectDir.cwd('./app');
18 | var destDir = projectDir.cwd('./build');
19 |
20 | var paths = {
21 | copyFromAppDir: [
22 | './node_modules/**',
23 | './libs/**',
24 | './assets/**',
25 | './helpers/**',
26 | './**/*.html',
27 | './**/*.+(jpg|png|svg)'
28 | ],
29 | };
30 |
31 | // -------------------------------------
32 | // Tasks
33 | // -------------------------------------
34 |
35 | gulp.task('clean', function () {
36 | return destDir.dirAsync('.', { empty: true });
37 | });
38 |
39 |
40 | var copyTask = function () {
41 | return projectDir.copyAsync('app', destDir.path(), {
42 | overwrite: true,
43 | matching: paths.copyFromAppDir
44 | });
45 | };
46 | gulp.task('copy', ['clean'], copyTask);
47 | gulp.task('copy-watch', copyTask);
48 |
49 |
50 | var bundleApplication = function () {
51 | return Q.all([
52 | bundle(srcDir.path('background.js'), destDir.path('background.js')),
53 | bundle(srcDir.path('app.js'), destDir.path('app.js')),
54 | ]);
55 | };
56 |
57 | var bundleSpecs = function () {
58 | return generateSpecImportsFile().then(function (specEntryPointPath) {
59 | return bundle(specEntryPointPath, destDir.path('spec.js'));
60 | });
61 | };
62 |
63 | var bundleTask = function () {
64 | if (utils.getEnvName() === 'test') {
65 | return bundleSpecs();
66 | }
67 | return bundleApplication();
68 | };
69 | gulp.task('bundle', ['clean'], bundleTask);
70 | gulp.task('bundle-watch', bundleTask);
71 |
72 |
73 | var lessTask = function () {
74 | return gulp.src('app/stylesheets/main.less')
75 | .pipe(plumber())
76 | .pipe(less())
77 | .pipe(gulp.dest(destDir.path('stylesheets')));
78 | };
79 | gulp.task('less', ['clean'], lessTask);
80 | gulp.task('less-watch', lessTask);
81 |
82 |
83 | gulp.task('environment', ['clean'], function () {
84 | var configFile = 'config/env_' + utils.getEnvName() + '.json';
85 | projectDir.copy(configFile, destDir.path('env.json'));
86 | });
87 |
88 |
89 | gulp.task('package-json', ['clean'], function () {
90 | var manifest = srcDir.read('package.json', 'json');
91 |
92 | // Add "dev" suffix to name, so Electron will write all data like cookies
93 | // and localStorage in separate places for production and development.
94 | if (utils.getEnvName() === 'development') {
95 | manifest.name += '-dev';
96 | manifest.productName += ' Dev';
97 | }
98 |
99 | destDir.write('package.json', manifest);
100 | });
101 |
102 |
103 | gulp.task('watch', function () {
104 | watch('app/**/*.js', batch(function (events, done) {
105 | gulp.start('bundle-watch', done);
106 | }));
107 | watch(paths.copyFromAppDir, { cwd: 'app' }, batch(function (events, done) {
108 | gulp.start('copy-watch', done);
109 | }));
110 | watch('app/**/*.less', batch(function (events, done) {
111 | gulp.start('less-watch', done);
112 | }));
113 | });
114 |
115 |
116 | gulp.task('build', ['bundle', 'less', 'copy', 'environment', 'package-json']);
117 |
--------------------------------------------------------------------------------
/tasks/build/bundle.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var pathUtil = require('path');
4 | var jetpack = require('fs-jetpack');
5 | var rollup = require('rollup');
6 | var Q = require('q');
7 |
8 | var nodeBuiltInModules = ['assert', 'buffer', 'child_process', 'cluster',
9 | 'console', 'constants', 'crypto', 'dgram', 'dns', 'domain', 'events',
10 | 'fs', 'http', 'https', 'module', 'net', 'os', 'path', 'process', 'punycode',
11 | 'querystring', 'readline', 'repl', 'stream', 'string_decoder', 'timers',
12 | 'tls', 'tty', 'url', 'util', 'v8', 'vm', 'zlib'];
13 |
14 | var electronBuiltInModules = ['electron'];
15 |
16 | var npmModulesUsedInApp = function () {
17 | var appManifest = require('../../app/package.json');
18 | return Object.keys(appManifest.dependencies);
19 | };
20 |
21 | var generateExternalModulesList = function () {
22 | return [].concat(nodeBuiltInModules, electronBuiltInModules, npmModulesUsedInApp());
23 | };
24 |
25 | module.exports = function (src, dest) {
26 | var deferred = Q.defer();
27 |
28 | rollup.rollup({
29 | entry: src,
30 | external: generateExternalModulesList(),
31 | }).then(function (bundle) {
32 | var jsFile = pathUtil.basename(dest);
33 | var result = bundle.generate({
34 | format: 'cjs',
35 | sourceMap: true,
36 | sourceMapFile: jsFile,
37 | });
38 | // Wrap code in self invoking function so the variables don't
39 | // pollute the global namespace.
40 | var isolatedCode = '(function () {' + result.code + '\n}());';
41 | return Q.all([
42 | jetpack.writeAsync(dest, isolatedCode + '\n//# sourceMappingURL=' + jsFile + '.map'),
43 | jetpack.writeAsync(dest + '.map', result.map.toString()),
44 | ]);
45 | }).then(function () {
46 | deferred.resolve();
47 | }).catch(function (err) {
48 | deferred.reject(err);
49 | });
50 |
51 | return deferred.promise;
52 | };
53 |
--------------------------------------------------------------------------------
/tasks/build/generate_spec_imports.js:
--------------------------------------------------------------------------------
1 | // Spec files are scattered through the whole project. Here we're searching
2 | // for them and generate one entry file which will run all the tests.
3 |
4 | 'use strict';
5 |
6 | var jetpack = require('fs-jetpack');
7 | var srcDir = jetpack.cwd('app');
8 |
9 | var fileName = 'spec.js.autogenerated';
10 | var fileBanner = "// This file is generated automatically.\n"
11 | + "// All your modifications to it will be lost (so don't do it).\n";
12 | var whatToInclude = [
13 | '*.spec.js',
14 | '!node_modules/**',
15 | ];
16 |
17 | module.exports = function () {
18 | return srcDir.findAsync('.', { matching: whatToInclude })
19 | .then(function (specPaths) {
20 | var fileContent = specPaths.map(function (path) {
21 | return 'import "./' + path.replace(/\\/g, '/') + '";';
22 | }).join('\n');
23 | return srcDir.writeAsync(fileName, fileBanner + fileContent);
24 | })
25 | .then(function () {
26 | return srcDir.path(fileName);
27 | });
28 | };
29 |
--------------------------------------------------------------------------------
/tasks/install_native_module.js:
--------------------------------------------------------------------------------
1 | // Install native module from npm and compile it for Electron.
2 | // Usage: npm run install-native -- name_of_native_module
3 |
4 | 'use strict';
5 |
6 | var childProcess = require('child_process');
7 | var Q = require('q');
8 | var appDir = require('fs-jetpack').cwd(__dirname, '../app');
9 | var utils = require('./utils');
10 |
11 | var ensureElectronRebuildInstalled = function () {
12 | var deferred = Q.defer();
13 |
14 | try {
15 | // If require is successful it means module is already installed.
16 | require('electron-rebuild');
17 | deferred.resolve();
18 | } catch (err) {
19 | childProcess.spawn(utils.spawnablePath('npm'), [
20 | 'install', '--save-dev', 'electron-rebuild'
21 | ], {
22 | stdio: 'inherit'
23 | })
24 | .on('error', deferred.reject)
25 | .on('close', deferred.resolve);
26 | }
27 |
28 | return deferred.promise;
29 | };
30 |
31 | var ensurePostinstallRunsElectronRebuild = function () {
32 | var postinstallScript = 'node ../tasks/rebuild_native_modules';
33 |
34 | var appManifest = appDir.read('package.json', 'json');
35 |
36 | if (typeof appManifest.scripts === 'undefined') {
37 | appManifest.scripts = {};
38 | }
39 |
40 | // Let's do it 100% bulletproof and check if programmer didn't
41 | // pust some custom stuff into postinstall script already.
42 | if (typeof appManifest.scripts.postinstall === 'undefined') {
43 | appManifest.scripts.postinstall = postinstallScript;
44 | appDir.write('package.json', appManifest);
45 | } else if (appManifest.scripts.postinstall.indexOf(postinstallScript) === -1) {
46 | appManifest.scripts.postinstall += ' && ' + postinstallScript;
47 | appDir.write('package.json', appManifest);
48 | }
49 |
50 | return Q();
51 | };
52 |
53 | var installNativeModule = function () {
54 | var deferred = Q.defer();
55 | var moduleToInstallAndOtherParams = process.argv.slice(2);
56 |
57 | if (!moduleToInstallAndOtherParams.length === 0) {
58 | deferred.reject('Module name not specified! Correct usage is "npm run install-native -- name_of_native_module" (remember about space after "--").');
59 | } else {
60 | childProcess.spawn(
61 | utils.spawnablePath('npm'),
62 | ['install', '--save'].concat(moduleToInstallAndOtherParams),
63 | {
64 | cwd: appDir.cwd(),
65 | stdio: 'inherit'
66 | }
67 | )
68 | .on('error', deferred.reject)
69 | .on('close', deferred.resolve);
70 | }
71 |
72 | return deferred.promise;
73 | };
74 |
75 | var runRebuild = function () {
76 | var deferred = Q.defer();
77 |
78 | childProcess.spawn(utils.spawnablePath('npm'), [
79 | 'run', 'postinstall'
80 | ], {
81 | cwd: appDir.cwd(),
82 | stdio: 'inherit'
83 | })
84 | .on('error', deferred.reject)
85 | .on('close', deferred.resolve);
86 |
87 | return deferred.promise;
88 | };
89 |
90 | ensureElectronRebuildInstalled()
91 | .then(ensurePostinstallRunsElectronRebuild)
92 | .then(installNativeModule)
93 | .then(runRebuild)
94 | .catch(function (err) {
95 | console.error(err);
96 | });
97 |
--------------------------------------------------------------------------------
/tasks/rebuild_native_modules.js:
--------------------------------------------------------------------------------
1 | // Rebuilds native node modules for Electron.
2 | // More: https://github.com/atom/electron/blob/master/docs/tutorial/using-native-node-modules.md
3 |
4 | 'use strict';
5 |
6 | var path = require('path');
7 | var Q = require('q');
8 | var electron = require('electron-prebuilt');
9 | var electronPackage = require('electron-prebuilt/package.json');
10 | var rebuild = require('electron-rebuild');
11 |
12 | var pathToElectronNativeModules = path.join(__dirname, '../app/node_modules');
13 |
14 | rebuild.shouldRebuildNativeModules(electron)
15 | .then(function (shouldBuild) {
16 | if (!shouldBuild) {
17 | return true;
18 | }
19 |
20 | console.log('Rebuilding native modules for Electron...');
21 |
22 | return rebuild.installNodeHeaders(electronPackage.version)
23 | .then(function () {
24 | return rebuild.rebuildNativeModules(electronPackage.version, pathToElectronNativeModules);
25 | });
26 | })
27 | .then(function () {
28 | console.log('Rebuilding complete.');
29 | })
30 | .catch(function (err) {
31 | console.error("Rebuilding error!");
32 | console.error(err);
33 | });
34 |
--------------------------------------------------------------------------------
/tasks/release/linux.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var Q = require('q');
4 | var gulpUtil = require('gulp-util');
5 | var childProcess = require('child_process');
6 | var jetpack = require('fs-jetpack');
7 | var asar = require('asar');
8 | var utils = require('../utils');
9 |
10 | var projectDir;
11 | var releasesDir;
12 | var packName;
13 | var packDir;
14 | var tmpDir;
15 | var readyAppDir;
16 | var manifest;
17 |
18 | var init = function () {
19 | projectDir = jetpack;
20 | tmpDir = projectDir.dir('./tmp', { empty: true });
21 | releasesDir = projectDir.dir('./releases');
22 | manifest = projectDir.read('app/package.json', 'json');
23 | packName = utils.getReleasePackageName(manifest);
24 | packDir = tmpDir.dir(packName);
25 | readyAppDir = packDir.cwd('opt', manifest.name);
26 |
27 | return new Q();
28 | };
29 |
30 | var copyRuntime = function () {
31 | return projectDir.copyAsync('node_modules/electron-prebuilt/dist', readyAppDir.path(), { overwrite: true });
32 | };
33 |
34 | var packageBuiltApp = function () {
35 | var deferred = Q.defer();
36 |
37 | asar.createPackageWithOptions(projectDir.path('build'), readyAppDir.path('resources/app.asar'), {
38 | dot: true
39 | }, function () {
40 | deferred.resolve();
41 | });
42 |
43 | return deferred.promise;
44 | };
45 |
46 | var finalize = function () {
47 | // Create .desktop file from the template
48 | var desktop = projectDir.read('resources/linux/app.desktop');
49 | desktop = utils.replace(desktop, {
50 | name: manifest.name,
51 | productName: manifest.productName,
52 | description: manifest.description,
53 | version: manifest.version,
54 | author: manifest.author
55 | });
56 | packDir.write('usr/share/applications/' + manifest.name + '.desktop', desktop);
57 |
58 | // Copy icon
59 | projectDir.copy('resources/icon.png', readyAppDir.path('icon.png'));
60 |
61 | return new Q();
62 | };
63 |
64 | var renameApp = function () {
65 | return readyAppDir.renameAsync('electron', manifest.name);
66 | };
67 |
68 | var packToDebFile = function () {
69 | var deferred = Q.defer();
70 |
71 | var debFileName = packName + '.deb';
72 | var debPath = releasesDir.path(debFileName);
73 |
74 | gulpUtil.log('Creating DEB package... (' + debFileName + ')');
75 |
76 | // Counting size of the app in KiB
77 | var appSize = Math.round(readyAppDir.inspectTree('.').size / 1024);
78 |
79 | // Preparing debian control file
80 | var control = projectDir.read('resources/linux/DEBIAN/control');
81 | control = utils.replace(control, {
82 | name: manifest.name,
83 | description: manifest.description,
84 | version: manifest.version,
85 | author: manifest.author,
86 | size: appSize
87 | });
88 | packDir.write('DEBIAN/control', control);
89 |
90 | // Build the package...
91 | childProcess.exec('fakeroot dpkg-deb -Zxz --build ' + packDir.path().replace(/\s/g, '\\ ') + ' ' + debPath.replace(/\s/g, '\\ '),
92 | function (error, stdout, stderr) {
93 | if (error || stderr) {
94 | console.log('ERROR while building DEB package:');
95 | console.log(error);
96 | console.log(stderr);
97 | } else {
98 | gulpUtil.log('DEB package ready!', debPath);
99 | }
100 | deferred.resolve();
101 | });
102 |
103 | return deferred.promise;
104 | };
105 |
106 | var cleanClutter = function () {
107 | return tmpDir.removeAsync('.');
108 | };
109 |
110 | module.exports = function () {
111 | return init()
112 | .then(copyRuntime)
113 | .then(packageBuiltApp)
114 | .then(finalize)
115 | .then(renameApp)
116 | .then(packToDebFile)
117 | .then(cleanClutter)
118 | .catch(console.error);
119 | };
120 |
--------------------------------------------------------------------------------
/tasks/release/osx.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var Q = require('q');
4 | var gulpUtil = require('gulp-util');
5 | var jetpack = require('fs-jetpack');
6 | var asar = require('asar');
7 | var utils = require('../utils');
8 | var child_process = require('child_process');
9 |
10 | var projectDir;
11 | var releasesDir;
12 | var tmpDir;
13 | var finalAppDir;
14 | var manifest;
15 |
16 | var init = function () {
17 | projectDir = jetpack;
18 | tmpDir = projectDir.dir('./tmp', { empty: true });
19 | releasesDir = projectDir.dir('./releases');
20 | manifest = projectDir.read('app/package.json', 'json');
21 | finalAppDir = tmpDir.cwd(manifest.productName + '.app');
22 |
23 | return new Q();
24 | };
25 |
26 | var copyRuntime = function () {
27 | return projectDir.copyAsync('node_modules/electron-prebuilt/dist/Electron.app', finalAppDir.path());
28 | };
29 |
30 | var cleanupRuntime = function () {
31 | finalAppDir.remove('Contents/Resources/default_app');
32 | finalAppDir.remove('Contents/Resources/atom.icns');
33 | return new Q();
34 | };
35 |
36 | var packageBuiltApp = function () {
37 | var deferred = Q.defer();
38 |
39 | asar.createPackageWithOptions(projectDir.path('build'), finalAppDir.path('Contents/Resources/app.asar'), {
40 | dot: true
41 | }, function () {
42 | deferred.resolve();
43 | });
44 |
45 | return deferred.promise;
46 | };
47 |
48 | var finalize = function () {
49 | // Prepare main Info.plist
50 | var info = projectDir.read('resources/osx/Info.plist');
51 | info = utils.replace(info, {
52 | productName: manifest.productName,
53 | identifier: manifest.osx.identifier,
54 | version: manifest.version,
55 | build: manifest.osx.build,
56 | copyright: manifest.copyright,
57 | LSApplicationCategoryType: manifest.osx.LSApplicationCategoryType
58 | });
59 | finalAppDir.write('Contents/Info.plist', info);
60 |
61 | // Prepare Info.plist of Helper apps
62 | [' EH', ' NP', ''].forEach(function (helper_suffix) {
63 | info = projectDir.read('resources/osx/helper_apps/Info' + helper_suffix + '.plist');
64 | info = utils.replace(info, {
65 | productName: manifest.productName,
66 | identifier: manifest.identifier
67 | });
68 | finalAppDir.write('Contents/Frameworks/Electron Helper' + helper_suffix + '.app/Contents/Info.plist', info);
69 | });
70 |
71 | // Copy icon
72 | projectDir.copy('resources/osx/icon.icns', finalAppDir.path('Contents/Resources/icon.icns'));
73 |
74 | return new Q();
75 | };
76 |
77 | var renameApp = function () {
78 | // Rename helpers
79 | [' Helper EH', ' Helper NP', ' Helper'].forEach(function (helper_suffix) {
80 | finalAppDir.rename('Contents/Frameworks/Electron' + helper_suffix + '.app/Contents/MacOS/Electron' + helper_suffix, manifest.productName + helper_suffix );
81 | finalAppDir.rename('Contents/Frameworks/Electron' + helper_suffix + '.app', manifest.productName + helper_suffix + '.app');
82 | });
83 | // Rename application
84 | finalAppDir.rename('Contents/MacOS/Electron', manifest.productName);
85 | return new Q();
86 | };
87 |
88 | var signApp = function () {
89 | var identity = utils.getSigningId(manifest);
90 | var MASIdentity = utils.getMASSigningId(manifest);
91 | var MASInstallerIdentity = utils.getMASInstallerSigningId(manifest);
92 |
93 | if (utils.releaseForMAS()) {
94 | if (!MASIdentity || !MASInstallerIdentity) {
95 | gulpUtil.log('--mas-sign and --mas-installer-sign are required to release for Mac App Store!');
96 | process.exit(0);
97 | }
98 | var cmds = [
99 | 'codesign --deep -f -s "' + MASIdentity + '" --entitlements resources/osx/child.plist -v "' + finalAppDir.path() + '/Contents/Frameworks/Electron Framework.framework/Versions/A/Libraries/libffmpeg.dylib"',
100 | 'codesign --deep -f -s "' + MASIdentity + '" --entitlements resources/osx/child.plist -v "' + finalAppDir.path() + '/Contents/Frameworks/Electron Framework.framework/Versions/A/Libraries/libnode.dylib"',
101 | 'codesign --deep -f -s "' + MASIdentity + '" --entitlements resources/osx/child.plist -v "' + finalAppDir.path() + '/Contents/Frameworks/Electron Framework.framework/Versions/A"',
102 | 'codesign --deep -f -s "' + MASIdentity + '" --entitlements resources/osx/child.plist -v "' + finalAppDir.path() + '/Contents/Frameworks/' + manifest.productName + ' Helper.app/"',
103 | 'codesign --deep -f -s "' + MASIdentity + '" --entitlements resources/osx/child.plist -v "' + finalAppDir.path() + '/Contents/Frameworks/' + manifest.productName + ' Helper EH.app/"',
104 | 'codesign --deep -f -s "' + MASIdentity + '" --entitlements resources/osx/child.plist -v "' + finalAppDir.path() + '/Contents/Frameworks/' + manifest.productName + ' Helper NP.app/"'
105 | ];
106 |
107 | if (finalAppDir.exists('Contents/Frameworks/Squirrel.framework/Versions/A')) {
108 | // # Signing a non-MAS build.
109 | cmds.push('codesign --deep -f -s "' + MASIdentity + '" --entitlements resources/osx/child.plist "' + finalAppDir.path() + '/Contents/Frameworks/Mantle.framework/Versions/A"');
110 | cmds.push('codesign --deep -f -s "' + MASIdentity + '" --entitlements resources/osx/child.plist "' + finalAppDir.path() + '/Contents/Frameworks/ReactiveCocoa.framework/Versions/A"');
111 | cmds.push('codesign --deep -f -s "' + MASIdentity + '" --entitlements resources/osx/child.plist "' + finalAppDir.path() + '/Contents/Frameworks/Squirrel.framework/Versions/A"');
112 | }
113 |
114 | cmds.push('codesign -f -s "' + MASIdentity + '" --entitlements resources/osx/parent.plist -v "' + finalAppDir.path() + '"');
115 |
116 | cmds.push('productbuild --component "' + finalAppDir.path() + '" /Applications --sign "' + MASInstallerIdentity + '" "' + releasesDir.path(manifest.productName + '.pkg') + '"');
117 |
118 | var result = new Q();
119 | cmds.forEach(function (cmd) {
120 | result = result.then(function(result) {
121 | gulpUtil.log('Signing with:', cmd);
122 | return Q.nfcall(child_process.exec, cmd);
123 | });
124 | });
125 | result = result.then(function(result) {
126 | return new Q();
127 | });
128 | return result;
129 |
130 | } else if (identity) {
131 | var cmd = 'codesign --deep --force --sign "' + identity + '" "' + finalAppDir.path() + '"';
132 | gulpUtil.log('Signing with:', cmd);
133 | return Q.nfcall(child_process.exec, cmd);
134 | } else {
135 | return new Q();
136 | }
137 | };
138 |
139 | var packToDmgFile = function () {
140 | if (utils.releaseForMAS()) {
141 | return new Q();
142 | }
143 |
144 | var deferred = Q.defer();
145 |
146 | var appdmg = require('appdmg');
147 | var dmgName = utils.getReleasePackageName(manifest) + '.dmg';
148 |
149 | // Prepare appdmg config
150 | var dmgManifest = projectDir.read('resources/osx/appdmg.json');
151 | dmgManifest = utils.replace(dmgManifest, {
152 | productName: manifest.productName,
153 | appPath: finalAppDir.path(),
154 | dmgIcon: projectDir.path("resources/osx/dmg-icon.icns"),
155 | dmgBackground: projectDir.path("resources/osx/dmg-background.png")
156 | });
157 | tmpDir.write('appdmg.json', dmgManifest);
158 |
159 | // Delete DMG file with this name if already exists
160 | releasesDir.remove(dmgName);
161 |
162 | gulpUtil.log('Packaging to DMG file... (' + dmgName + ')');
163 |
164 | var readyDmgPath = releasesDir.path(dmgName);
165 | appdmg({
166 | source: tmpDir.path('appdmg.json'),
167 | target: readyDmgPath
168 | })
169 | .on('error', function (err) {
170 | console.error(err);
171 | })
172 | .on('finish', function () {
173 | gulpUtil.log('DMG file ready!', readyDmgPath);
174 | deferred.resolve();
175 | });
176 |
177 | return deferred.promise;
178 | };
179 |
180 | var cleanClutter = function () {
181 | return tmpDir.removeAsync('.');
182 | };
183 |
184 | module.exports = function () {
185 | return init()
186 | .then(copyRuntime)
187 | .then(cleanupRuntime)
188 | .then(packageBuiltApp)
189 | .then(finalize)
190 | .then(renameApp)
191 | .then(signApp)
192 | .then(packToDmgFile)
193 | .then(cleanClutter)
194 | .catch(console.error);
195 | };
196 |
--------------------------------------------------------------------------------
/tasks/release/release.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var gulp = require('gulp');
4 | var utils = require('../utils');
5 |
6 | var releaseForOs = {
7 | osx: require('./osx'),
8 | linux: require('./linux'),
9 | windows: require('./windows'),
10 | };
11 |
12 | gulp.task('release', ['build'], function () {
13 | return releaseForOs[utils.os()]();
14 | });
15 |
--------------------------------------------------------------------------------
/tasks/release/windows.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var Q = require('q');
4 | var gulpUtil = require('gulp-util');
5 | var childProcess = require('child_process');
6 | var jetpack = require('fs-jetpack');
7 | var asar = require('asar');
8 | var utils = require('../utils');
9 |
10 | var projectDir;
11 | var tmpDir;
12 | var releasesDir;
13 | var readyAppDir;
14 | var manifest;
15 |
16 | var init = function () {
17 | projectDir = jetpack;
18 | tmpDir = projectDir.dir('./tmp', { empty: true });
19 | releasesDir = projectDir.dir('./releases');
20 | manifest = projectDir.read('app/package.json', 'json');
21 | readyAppDir = tmpDir.cwd(manifest.name);
22 |
23 | return new Q();
24 | };
25 |
26 | var copyRuntime = function () {
27 | return projectDir.copyAsync('node_modules/electron-prebuilt/dist', readyAppDir.path(), { overwrite: true });
28 | };
29 |
30 | var cleanupRuntime = function () {
31 | return readyAppDir.removeAsync('resources/default_app');
32 | };
33 |
34 | var packageBuiltApp = function () {
35 | var deferred = Q.defer();
36 |
37 | asar.createPackageWithOptions(projectDir.path('build'), readyAppDir.path('resources/app.asar'), {
38 | dot: true
39 | }, function () {
40 | deferred.resolve();
41 | });
42 |
43 | return deferred.promise;
44 | };
45 |
46 | var finalize = function () {
47 | var deferred = Q.defer();
48 |
49 | projectDir.copy('resources/windows/icon.ico', readyAppDir.path('icon.ico'));
50 |
51 | // Replace Electron icon for your own.
52 | var rcedit = require('rcedit');
53 | rcedit(readyAppDir.path('electron.exe'), {
54 | 'icon': projectDir.path('resources/windows/icon.ico'),
55 | 'version-string': {
56 | 'ProductName': manifest.productName,
57 | 'FileDescription': manifest.description,
58 | 'ProductVersion': manifest.version,
59 | 'CompanyName': manifest.author, // it might be better to add another field to package.json for this
60 | 'LegalCopyright': manifest.copyright,
61 | 'OriginalFilename': manifest.productName + '.exe'
62 | }
63 | }, function (err) {
64 | if (!err) {
65 | deferred.resolve();
66 | }
67 | });
68 |
69 | return deferred.promise;
70 | };
71 |
72 | var renameApp = function () {
73 | return readyAppDir.renameAsync('electron.exe', manifest.productName + '.exe');
74 | };
75 |
76 | var createInstaller = function () {
77 | var deferred = Q.defer();
78 |
79 | var finalPackageName = utils.getReleasePackageName(manifest) + '.exe';
80 | var installScript = projectDir.read('resources/windows/installer.nsi');
81 |
82 | installScript = utils.replace(installScript, {
83 | name: manifest.name,
84 | productName: manifest.productName,
85 | author: manifest.author,
86 | version: manifest.version,
87 | src: readyAppDir.path(),
88 | dest: releasesDir.path(finalPackageName),
89 | icon: readyAppDir.path('icon.ico'),
90 | setupIcon: projectDir.path('resources/windows/setup-icon.ico'),
91 | banner: projectDir.path('resources/windows/setup-banner.bmp'),
92 | });
93 | tmpDir.write('installer.nsi', installScript);
94 |
95 | gulpUtil.log('Building installer with NSIS... (' + finalPackageName + ')');
96 |
97 | // Remove destination file if already exists.
98 | releasesDir.remove(finalPackageName);
99 |
100 | // Note: NSIS have to be added to PATH (environment variables).
101 | var nsis = childProcess.spawn('makensis', [
102 | tmpDir.path('installer.nsi')
103 | ], {
104 | stdio: 'inherit'
105 | });
106 | nsis.on('error', function (err) {
107 | if (err.message === 'spawn makensis ENOENT') {
108 | throw "Can't find NSIS. Are you sure you've installed it and"
109 | + " added to PATH environment variable?";
110 | } else {
111 | throw err;
112 | }
113 | });
114 | nsis.on('close', function () {
115 | gulpUtil.log('Installer ready!', releasesDir.path(finalPackageName));
116 | deferred.resolve();
117 | });
118 |
119 | return deferred.promise;
120 | };
121 |
122 | var cleanClutter = function () {
123 | return tmpDir.removeAsync('.');
124 | };
125 |
126 | module.exports = function () {
127 | return init()
128 | .then(copyRuntime)
129 | .then(cleanupRuntime)
130 | .then(packageBuiltApp)
131 | .then(finalize)
132 | .then(renameApp)
133 | .then(createInstaller)
134 | .then(cleanClutter)
135 | .catch(console.error);
136 | };
137 |
--------------------------------------------------------------------------------
/tasks/start.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var childProcess = require('child_process');
4 | var electron = require('electron-prebuilt');
5 | var gulp = require('gulp');
6 |
7 | gulp.task('start', ['build', 'watch'], function () {
8 | childProcess.spawn(electron, ['./build'], {
9 | stdio: 'inherit'
10 | })
11 | .on('close', function () {
12 | // User closed the app. Kill the host process.
13 | process.exit();
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/tasks/utils.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var argv = require('yargs').argv;
4 | var os = require('os');
5 |
6 | module.exports.os = function () {
7 | switch (os.platform()) {
8 | case 'darwin':
9 | return 'osx';
10 | case 'linux':
11 | return 'linux';
12 | case 'win32':
13 | return 'windows';
14 | }
15 | return 'unsupported';
16 | };
17 |
18 | module.exports.replace = function (str, patterns) {
19 | Object.keys(patterns).forEach(function (pattern) {
20 | var matcher = new RegExp('{{' + pattern + '}}', 'g');
21 | str = str.replace(matcher, patterns[pattern]);
22 | });
23 | return str;
24 | };
25 |
26 | module.exports.getReleasePackageName = function(manifest) {
27 | return module.exports.replace(manifest.packageNameTemplate, {
28 | name: manifest.name,
29 | version: manifest.version,
30 | build: manifest.build,
31 | productName: manifest.productName,
32 | platform: process.platform,
33 | arch: process.arch
34 | });
35 | };
36 |
37 | module.exports.getEnvName = function () {
38 | return argv.env || 'development';
39 | };
40 |
41 | module.exports.getSigningId = function (manifest) {
42 | return argv.sign || (manifest.osx.codeSignIdentitiy ? manifest.osx.codeSignIdentitiy.dmg : undefined);
43 | };
44 |
45 | module.exports.getMASSigningId = function (manifest) {
46 | return argv['mas-sign'] || (manifest.osx.codeSignIdentitiy ? manifest.osx.codeSignIdentitiy.MAS : undefined);
47 | };
48 |
49 | module.exports.getMASInstallerSigningId = function (manifest) {
50 | return argv['mas-installer-sign'] || (manifest.osx.codeSignIdentitiy ? manifest.osx.codeSignIdentitiy.MASInstaller : undefined);
51 | };
52 |
53 | module.exports.releaseForMAS = function () {
54 | return !!argv.mas;
55 | };
56 |
57 | // Fixes https://github.com/nodejs/node-v0.x-archive/issues/2318
58 | module.exports.spawnablePath = function (path) {
59 | if (process.platform === 'win32') {
60 | return path + '.cmd';
61 | }
62 | return path;
63 | };
64 |
--------------------------------------------------------------------------------