mirror of
https://github.com/netzbegruenung/Rocket.Chat.Electron.git
synced 2024-05-05 07:23:40 +02:00
Merge remote-tracking branch 'szwacz/master' into update-upstream
# Conflicts: # .gitignore # .travis.yml # LICENSE # README.md # app/app.html # app/background.js # app/images/osx/dmg-icon.icns # app/package.json # app/vendor/electron_boilerplate/window_state.js # appveyor.yml # build/icon.icns # build/icon.ico # build/icons/512x512.png # package.json # resources/linux/app.desktop # resources/osx/Info.plist # resources/windows/installer.nsi # src/hello_world/hello_world.js # tasks/build/build.js # tasks/rebuild_native_modules.js # tasks/release/linux.js # tasks/release/osx.js # tasks/release/windows.js # tasks/start.js # tasks/utils.js
This commit is contained in:
commit
feff7c2782
15
.gitignore
vendored
15
.gitignore
vendored
|
@ -1,9 +1,20 @@
|
|||
node_modules
|
||||
*.log
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
*.log
|
||||
*.autogenerated
|
||||
|
||||
# ignore everything in 'app' folder what had been generated from 'src' folder
|
||||
/app/stylesheets
|
||||
/app/app.js
|
||||
/app/background.js
|
||||
/app/env.json
|
||||
/app/**/*.map
|
||||
|
||||
/dist
|
||||
|
||||
/coverage
|
||||
|
||||
/app/spec.js
|
||||
/build/
|
||||
/releases/
|
||||
/tmp/
|
||||
|
|
81
.travis.yml
81
.travis.yml
|
@ -1,59 +1,24 @@
|
|||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
dist: trusty
|
||||
sudo: required
|
||||
language: node_js
|
||||
node_js:
|
||||
- '6'
|
||||
script:
|
||||
- npm run release
|
||||
deploy:
|
||||
provider: releases
|
||||
api_key:
|
||||
secure: DHzFJ9JGNAdeTfTmr2vLJFajTL0XHEYG1OKeD4DQhxB1mGDVLF2FX2T0PrRoqD1S084acnEKN1nElMn/NWc6eNPVksqQlC+8kVtYsX6EMOoiT8YnsQW7TbAsBEp30a/cuNWLfUFEmwsXN6zQQMgyF5vBoMjsycxDXq9MF5AQ9Y5BXM0Dky/v5fBmKyoVl76G/L6RkXM4SaTpthwfFmYlrAC9ctbCgA337qS3SmNLjh/EzdvgAkI9mKxewBizp1/T8hzagcOXs+UoDm/avhbZTTxfxf03PcBHs6/YwlVGKWnsIKD+BP0N7kt4q7QjXmetx5UDvKiYk269lyapqgJgF9QhODjuxufK8HD9dkOfDsG2kC0VR0BK7fHgkFCEItp6rvWwvvSDKVJN7ogT9V8XzQjeYTWwc03B58zu0f9hS4x5OMrZrs3z+NsZ/Qr+yaEjmJAiazkJPOpfPyQB0EllnPY+uugzo9iUMLmK99aYTgK0ZKOOHtJh0ziv6aQgKK6L8i9k7A4t99SfLlaKPCNITx4FH8TbLts6Oxm87iNhTQPafpOZUGUeuHtJywK1Rj+CfYffqIpZahG8jRkfllqMoLZ0QmBXE+CD7aPkTN3MF1Tj38Dbe3HjOGl8oJfS2VKH8dH6p6e9BBTXvU/bOKNCb8FBjNT2SY8UYAxnlDmzJDA=
|
||||
file:
|
||||
- "$TRAVIS_BUILD_DIR/releases/rocketchat-$TRAVIS_TAG-linux-x64.deb"
|
||||
- "$TRAVIS_BUILD_DIR/releases/rocketchat-$TRAVIS_TAG-linux.x86_64.rpm"
|
||||
skip_cleanup: true
|
||||
on:
|
||||
tags: true
|
||||
all_branches: true
|
||||
- os: osx
|
||||
osx_image: xcode7.3.1
|
||||
language: generic
|
||||
node_js:
|
||||
- '6'
|
||||
script:
|
||||
- echo $CERTIFICATE_OSX_P12_DEVELOPER_ID_APPLICATION | base64 --decode > CERTIFICATE_OSX_P12_DEVELOPER_ID_APPLICATION.p12
|
||||
- security create-keychain -p KEYCHAIN_PASSWORD build.keychain
|
||||
- security default-keychain -s build.keychain
|
||||
- security unlock-keychain -p KEYCHAIN_PASSWORD build.keychain
|
||||
- security import CERTIFICATE_OSX_P12_DEVELOPER_ID_APPLICATION.p12 -k build.keychain -P '' -T /usr/bin/codesign
|
||||
- security find-identity -v
|
||||
- npm run release
|
||||
deploy:
|
||||
provider: releases
|
||||
api_key:
|
||||
secure: DHzFJ9JGNAdeTfTmr2vLJFajTL0XHEYG1OKeD4DQhxB1mGDVLF2FX2T0PrRoqD1S084acnEKN1nElMn/NWc6eNPVksqQlC+8kVtYsX6EMOoiT8YnsQW7TbAsBEp30a/cuNWLfUFEmwsXN6zQQMgyF5vBoMjsycxDXq9MF5AQ9Y5BXM0Dky/v5fBmKyoVl76G/L6RkXM4SaTpthwfFmYlrAC9ctbCgA337qS3SmNLjh/EzdvgAkI9mKxewBizp1/T8hzagcOXs+UoDm/avhbZTTxfxf03PcBHs6/YwlVGKWnsIKD+BP0N7kt4q7QjXmetx5UDvKiYk269lyapqgJgF9QhODjuxufK8HD9dkOfDsG2kC0VR0BK7fHgkFCEItp6rvWwvvSDKVJN7ogT9V8XzQjeYTWwc03B58zu0f9hS4x5OMrZrs3z+NsZ/Qr+yaEjmJAiazkJPOpfPyQB0EllnPY+uugzo9iUMLmK99aYTgK0ZKOOHtJh0ziv6aQgKK6L8i9k7A4t99SfLlaKPCNITx4FH8TbLts6Oxm87iNhTQPafpOZUGUeuHtJywK1Rj+CfYffqIpZahG8jRkfllqMoLZ0QmBXE+CD7aPkTN3MF1Tj38Dbe3HjOGl8oJfS2VKH8dH6p6e9BBTXvU/bOKNCb8FBjNT2SY8UYAxnlDmzJDA=
|
||||
file:
|
||||
- "$TRAVIS_BUILD_DIR/releases/rocketchat-$TRAVIS_TAG-darwin-x64.dmg"
|
||||
skip_cleanup: true
|
||||
on:
|
||||
tags: true
|
||||
all_branches: true
|
||||
git:
|
||||
depth: 1
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- rpm
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
env:
|
||||
- NODE_VERSION="6.3.0"
|
||||
|
||||
before_script:
|
||||
- rm -rf releases
|
||||
- travis_retry npm install
|
||||
- |
|
||||
perl -pe "s/(\"version\": )\"([0-9.]+)\"/ q{\"version\": \"${TRAVIS_TAG:-0.0.0}\"} /ge" -i app/package.json
|
||||
- |
|
||||
perl -pe "s/(\"build\": )\"([0-9.]+)\"/ q{\"build\": \"${TRAVIS_BUILD_NUMBER:-0}\"} /ge" -i app/package.json
|
||||
after_script:
|
||||
- ls releases/
|
||||
- chmod +x ./scripts/travis-build.sh
|
||||
|
||||
script: ./scripts/travis-build.sh
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
|
||||
notifications:
|
||||
email:
|
||||
on_success: never
|
||||
on_failure: change
|
||||
|
|
1
LICENSE
1
LICENSE
|
@ -19,4 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
|
176
README.md
176
README.md
|
@ -1,111 +1,127 @@
|
|||
Rocket.Chat.Electron
|
||||
==============
|
||||
# electron-boilerplate [![Build Status](https://travis-ci.org/szwacz/electron-boilerplate.svg?branch=master)](https://travis-ci.org/szwacz/electron-boilerplate) [![Build status](https://ci.appveyor.com/api/projects/status/s9htc1k5ojkn08fr?svg=true)](https://ci.appveyor.com/project/szwacz/electron-boilerplate)
|
||||
|
||||
All of Rocket.Chat's Desktop Apps - for Windows, Mac OSX, and Linux are based on the [Electron platform from GitHub](https://github.com/electron/electron). This is the source code base for all desktop apps.
|
||||
A minimalistic yet comprehensive boilerplate application for [Electron runtime](http://electron.atom.io). Tested on macOS, Windows and Linux.
|
||||
|
||||
### IMPORTANT
|
||||
This project does not impose on you any framework (like Angular or React). It tries to give you only the 'electron' part of technology stack so you can pick your favorite technologies to build the actual app.
|
||||
|
||||
Please join the community server channel for Rocket.Chat Electron app users for feedback, interactions, and important notification regarding this code:
|
||||
# Quick start
|
||||
|
||||
https://demo.rocket.chat/channel/desktopclient
|
||||
The only development dependency of this project is [Node.js](https://nodejs.org), so just make sure you have it installed.
|
||||
Then type few commands known to every Node developer...
|
||||
```
|
||||
git clone https://github.com/szwacz/electron-boilerplate.git
|
||||
cd electron-boilerplate
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
... and boom! You have a running desktop application on your screen.
|
||||
|
||||
# Structure of the project
|
||||
|
||||
The application is split between two main folders...
|
||||
|
||||
`src` - this folder is intended for files which need to be transpiled or compiled (files which can't be used directly by Electron).
|
||||
|
||||
`app` - contains all static assets (put here images, css, html etc.) which don't need any pre-processing.
|
||||
|
||||
The build process compiles all stuff from the `src` folder and puts it into the `app` folder, so after the build has finished, your `app` folder contains the full, runnable application.
|
||||
|
||||
Treat `src` and `app` folders like two halves of one bigger thing.
|
||||
|
||||
The drawback of this design is that `app` folder contains some files which should be git-ignored and some which shouldn't (see `.gitignore` file). But thanks to this two-folders split development builds are much (much!) faster.
|
||||
|
||||
# Development
|
||||
|
||||
#### Installation
|
||||
|
||||
```
|
||||
npm install
|
||||
```
|
||||
It will also download Electron runtime, and install dependencies for second `package.json` file inside `app` folder.
|
||||
|
||||
Debian users need to make sure they have the package `libxss-dev` installed.
|
||||
|
||||
#### Starting the app
|
||||
## Starting the app
|
||||
|
||||
```
|
||||
npm start
|
||||
```
|
||||
|
||||
# Structure of the project
|
||||
## Upgrading Electron version
|
||||
|
||||
There are **two** `package.json` files:
|
||||
The version of Electron runtime your app is using is declared in `package.json`:
|
||||
```json
|
||||
"devDependencies": {
|
||||
"electron": "1.4.7"
|
||||
}
|
||||
```
|
||||
Side note: [Electron authors advise](http://electron.atom.io/docs/tutorial/electron-versioning/) to use fixed version here.
|
||||
|
||||
#### 1. For development
|
||||
Sits at root of the application. Just used for development dependencies. **This file is not distributed with real application!**
|
||||
## The build pipeline
|
||||
|
||||
Build process is founded upon [gulp](https://github.com/gulpjs/gulp) task runner and [rollup](https://github.com/rollup/rollup) bundler. There are two entry files for your code: `src/background.js` and `src/app.js`. Rollup will follow all `import` statements starting from those files and compile code of the whole dependency tree into one `.js` file for each entry point.
|
||||
|
||||
#### 2. For your application
|
||||
Sits on path: `app/package.json`. This is **real** manifest of your application. App dependencies declared here.
|
||||
You can [add as many more entry points as you like](https://github.com/szwacz/electron-boilerplate/blob/master/tasks/build_app.js#L16) (e.g. to split your app into modules).
|
||||
|
||||
### Project's folders
|
||||
By the way, [rollup has a lot of plugins](https://github.com/rollup/rollup/wiki/Plugins). You can add them in [this file](https://github.com/szwacz/electron-boilerplate/blob/master/tasks/bundle.js).
|
||||
|
||||
- `app` - code of your application goes here.
|
||||
- `config` - place where you can declare environment specific stuff for your app.
|
||||
- `build` - in this folder lands built, runnable application.
|
||||
- `releases` - ready for distribution installers will land here.
|
||||
- `resources` - resources needed for particular operating system.
|
||||
- `tasks` - build and development environment scripts.
|
||||
## Adding npm modules to your app
|
||||
|
||||
Remember to respect the split between `dependencies` and `devDependencies` in `package.json` file. Only modules listed in `dependencies` will be included into distributable app.
|
||||
|
||||
Side note: If the module you want to use in your app is a native one (not pure JavaScript but compiled C code or something) you should first run `npm install name_of_npm_module --save` and then `npm run postinstall` to rebuild the module for Electron. This needs to be done only once when you're first time installing the module. Later on postinstall script will fire automatically with every `npm install`.
|
||||
|
||||
## Working with modules
|
||||
|
||||
Thanks to [rollup](https://github.com/rollup/rollup) you can (and should) use ES6 modules for all code in `src` folder. But because ES6 modules still aren't natively supported you can't use them in the `app` folder.
|
||||
|
||||
Use ES6 syntax in the `src` folder like this:
|
||||
```js
|
||||
import myStuff from './my_lib/my_stuff';
|
||||
```
|
||||
|
||||
But use CommonJS syntax in `app` folder. So the code from above should look as follows:
|
||||
```js
|
||||
var myStuff = require('./my_lib/my_stuff');
|
||||
```
|
||||
|
||||
# Testing
|
||||
|
||||
## Unit tests
|
||||
|
||||
```
|
||||
npm test
|
||||
```
|
||||
|
||||
Using [electron-mocha](https://github.com/jprichardson/electron-mocha) test runner with the [chai](http://chaijs.com/api/assert/) assertion library. This task searches for all files in `src` directory which respect pattern `*.spec.js`.
|
||||
|
||||
## End to end tests
|
||||
|
||||
```
|
||||
npm run e2e
|
||||
```
|
||||
|
||||
Using [mocha](https://mochajs.org/) test runner and [spectron](http://electron.atom.io/spectron/). This task searches for all files in `e2e` directory which respect pattern `*.e2e.js`.
|
||||
|
||||
## Code coverage
|
||||
|
||||
```
|
||||
npm run coverage
|
||||
```
|
||||
|
||||
Using [istanbul](http://gotwarlost.github.io/istanbul/) code coverage tool.
|
||||
|
||||
You can set the reporter(s) by setting `ISTANBUL_REPORTERS` environment variable (defaults to `text-summary` and `html`). The report directory can be set with `ISTANBUL_REPORT_DIR` (defaults to `coverage`).
|
||||
|
||||
## Continuous integration
|
||||
|
||||
Electron [can be plugged](https://github.com/atom/electron/blob/master/docs/tutorial/testing-on-headless-ci.md) into CI systems. Here two CIs are preconfigured for you. [Travis CI](https://travis-ci.org/) tests on macOS and Linux, [App Veyor](https://www.appveyor.com) tests on Windows.
|
||||
|
||||
# Making a release
|
||||
|
||||
**Note:** There are various icon and bitmap files in `resources` directory. Those are used in installers and are intended to be replaced by your own graphics.
|
||||
To package your app into an installer use command:
|
||||
|
||||
To make ready for distribution installer use command:
|
||||
```
|
||||
npm run release
|
||||
```
|
||||
It will start the packaging process for operating system you are running this command on. Ready for distribution file will be outputted to `releases` directory.
|
||||
|
||||
You can create Windows installer only when running on Windows, the same is true for Linux and OSX. So to generate all three installers you need all three operating systems.
|
||||
It will start the packaging process for operating system you are running this command on. Ready for distribution file will be outputted to `dist` directory.
|
||||
|
||||
## Mac only
|
||||
You can create Windows installer only when running on Windows, the same is true for Linux and macOS. So to generate all three installers you need all three operating systems.
|
||||
|
||||
#### App signing
|
||||
All packaging actions are handled by [electron-builder](https://github.com/electron-userland/electron-builder). It has a lot of [customization options](https://github.com/electron-userland/electron-builder/wiki/Options), which you can declare under ["build" key in package.json file](https://github.com/szwacz/electron-boilerplate/blob/master/package.json#L2).
|
||||
|
||||
The Mac release supports [code signing](https://developer.apple.com/library/mac/documentation/Security/Conceptual/CodeSigningGuide/Procedures/Procedures.html). To sign the `.app` in the release image, include the certificate ID in the command as so,
|
||||
```shell
|
||||
npm run release -- --sign DX85ENM22A
|
||||
```
|
||||
# License
|
||||
|
||||
#### Mac App Store
|
||||
You should install the Electron build for MAS
|
||||
```
|
||||
export npm_config_platform=mas
|
||||
rm -rf node_modules
|
||||
npm install
|
||||
```
|
||||
|
||||
To sign your app for Mac App Store
|
||||
```shell
|
||||
npm run release -- --mas --mas-sign "3rd Party Mac Developer Application: Company Name (APPIDENTITY)" --mas-installer-sign "3rd Party Mac Developer Installer: Company Name (APPIDENTITY)"
|
||||
```
|
||||
|
||||
Or edit the `app/package.json`, remove the `//` from `//codeSignIdentitiy` and update the values with your sign indentities
|
||||
```json
|
||||
"//codeSignIdentitiy": {
|
||||
"dmg": "Developer ID Application: Company Name (APPIDENTITY)",
|
||||
"MAS": "3rd Party Mac Developer Application: Company Name (APPIDENTITY)",
|
||||
"MASInstaller": "3rd Party Mac Developer Installer: Company Name (APPIDENTITY)"
|
||||
}
|
||||
```
|
||||
|
||||
You can change the application category too
|
||||
```json
|
||||
"LSApplicationCategoryType": "public.app-category.productivity"
|
||||
```
|
||||
|
||||
If you insert your indentities in the package.json you can compile for MAS like
|
||||
```
|
||||
npm run release -- --mas
|
||||
```
|
||||
|
||||
## Windows only
|
||||
|
||||
#### Installer
|
||||
|
||||
The installer is built using [NSIS](http://nsis.sourceforge.net). You have to install NSIS version 3.0, and add its folder to PATH in Environment Variables, so it is reachable to scripts in this project. For example, `C:\Program Files (x86)\NSIS`.
|
||||
|
||||
#### 32-bit build on 64-bit Windows
|
||||
|
||||
There are still a lot of 32-bit Windows installations in use. If you want to support those systems and have 64-bit OS make sure you've installed 32-bit (instead of 64-bit) Node version. There are [versions managers](https://github.com/coreybutler/nvm-windows) if you feel the need for both architectures on the same machine.
|
||||
Released under the MIT license.
|
||||
|
|
15
app/env.js
15
app/env.js
|
@ -1,15 +0,0 @@
|
|||
// Simple module exposes environment variables to rest of the code.
|
||||
|
||||
import jetpack from 'fs-jetpack';
|
||||
|
||||
var app;
|
||||
if (process.type === 'renderer') {
|
||||
app = require('electron').remote.app;
|
||||
} else {
|
||||
app = require('electron').app;
|
||||
}
|
||||
var appDir = jetpack.cwd(app.getAppPath());
|
||||
|
||||
var manifest = appDir.read('package.json', 'json');
|
||||
|
||||
export default manifest.env;
|
|
@ -8,6 +8,10 @@
|
|||
var Menu = remote.Menu;
|
||||
var MenuItem = remote.MenuItem;
|
||||
|
||||
var isAnyTextSelected = function () {
|
||||
return window.getSelection().toString() !== '';
|
||||
};
|
||||
|
||||
var cut = new MenuItem({
|
||||
label: "Cut",
|
||||
click: function () {
|
||||
|
@ -29,21 +33,27 @@
|
|||
}
|
||||
});
|
||||
|
||||
var textMenu = new Menu();
|
||||
textMenu.append(cut);
|
||||
textMenu.append(copy);
|
||||
textMenu.append(paste);
|
||||
var normalMenu = new Menu();
|
||||
normalMenu.append(copy);
|
||||
|
||||
var textEditingMenu = new Menu();
|
||||
textEditingMenu.append(cut);
|
||||
textEditingMenu.append(copy);
|
||||
textEditingMenu.append(paste);
|
||||
|
||||
document.addEventListener('contextmenu', function (e) {
|
||||
|
||||
switch (e.target.nodeName) {
|
||||
case 'TEXTAREA':
|
||||
case 'INPUT':
|
||||
e.preventDefault();
|
||||
textMenu.popup(remote.getCurrentWindow());
|
||||
textEditingMenu.popup(remote.getCurrentWindow());
|
||||
break;
|
||||
default:
|
||||
if (isAnyTextSelected()) {
|
||||
e.preventDefault();
|
||||
normalMenu.popup(remote.getCurrentWindow());
|
||||
}
|
||||
}
|
||||
|
||||
}, false);
|
||||
|
||||
}());
|
|
@ -35,10 +35,10 @@
|
|||
} else if (element.parentElement) {
|
||||
checkDomElement(element.parentElement);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
checkDomElement(e.target);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('click', supportExternalLinks, false);
|
||||
}());
|
Binary file not shown.
|
@ -1,29 +0,0 @@
|
|||
{
|
||||
"name": "rocketchat",
|
||||
"productName": "Rocket.Chat+",
|
||||
"identifier": "chat.rocket",
|
||||
"description": "Rocket.Chat Native Cross-Platform Desktop Application via Electron.",
|
||||
"version": "2.0.2",
|
||||
"build": "1",
|
||||
"author": "Rocket.Chat Support <support@rocket.chat>",
|
||||
"copyright": "© 2016, Rocket.Chat",
|
||||
"main": "background.js",
|
||||
"dependencies": {
|
||||
"@paulcbetts/system-idle-time": "^1.0.4",
|
||||
"ffi": "^2.2.0",
|
||||
"electron-notification-shim": "^1.1.0",
|
||||
"electron-toaster": "^2.0.2",
|
||||
"fs-jetpack": "^0.10.5",
|
||||
"spellchecker": "3.3.1"
|
||||
},
|
||||
"packageNameTemplate": "{{name}}-{{version}}-{{platform}}-{{arch}}",
|
||||
"codeSignIdentity": {
|
||||
"dmg": "Developer ID Application: Konecty Informatica Ltda (DX85ENM22A)",
|
||||
"MAS": "3rd Party Mac Developer Application: Konecty Informatica Ltda (DX85ENM22A)",
|
||||
"MASInstaller": "3rd Party Mac Developer Installer: Konecty Informatica Ltda (DX85ENM22A)"
|
||||
},
|
||||
"LSApplicationCategoryType": "public.app-category.productivity",
|
||||
"scripts": {
|
||||
"postinstall": "node ../tasks/rebuild_native_modules"
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Jasmine Spec Runner</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="vendor/jasmine/jasmine.css">
|
||||
|
||||
<script src="vendor/jasmine/jasmine.js"></script>
|
||||
<script src="vendor/jasmine/jasmine-html.js"></script>
|
||||
<script src="vendor/jasmine/boot.js"></script>
|
||||
<script src="spec.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
31
app/vendor/electron_boilerplate/dev_helper.js
vendored
31
app/vendor/electron_boilerplate/dev_helper.js
vendored
|
@ -1,31 +0,0 @@
|
|||
import { app, Menu, BrowserWindow } from 'electron';
|
||||
|
||||
var setDevMenu = function () {
|
||||
var devMenu = Menu.buildFromTemplate([{
|
||||
label: 'Development',
|
||||
submenu: [{
|
||||
label: 'Reload',
|
||||
accelerator: 'CmdOrCtrl+R',
|
||||
click: function () {
|
||||
BrowserWindow.getFocusedWindow().webContents.reloadIgnoringCache();
|
||||
}
|
||||
},{
|
||||
label: 'Toggle DevTools',
|
||||
accelerator: 'Alt+CmdOrCtrl+I',
|
||||
click: function () {
|
||||
BrowserWindow.getFocusedWindow().toggleDevTools();
|
||||
}
|
||||
},{
|
||||
label: 'Quit',
|
||||
accelerator: 'CmdOrCtrl+Q',
|
||||
click: function () {
|
||||
app.quit();
|
||||
}
|
||||
}]
|
||||
}]);
|
||||
Menu.setApplicationMenu(devMenu);
|
||||
};
|
||||
|
||||
export default {
|
||||
setDevMenu: setDevMenu,
|
||||
}
|
52
app/vendor/electron_boilerplate/window_state.js
vendored
52
app/vendor/electron_boilerplate/window_state.js
vendored
|
@ -1,52 +0,0 @@
|
|||
// Simple module to help you remember the size and position of windows.
|
||||
// Can be used for more than one window, just construct many
|
||||
// instances of it and give each different name.
|
||||
|
||||
import { app } from 'electron';
|
||||
import jetpack from 'fs-jetpack';
|
||||
|
||||
export default function (name, defaults) {
|
||||
|
||||
var userDataDir = jetpack.cwd(app.getPath('userData'));
|
||||
var stateStoreFile = 'window-state-' + name +'.json';
|
||||
var state = {
|
||||
width: defaults.width,
|
||||
height: defaults.height
|
||||
};
|
||||
|
||||
try {
|
||||
var loadedState = userDataDir.read(stateStoreFile, 'json');
|
||||
if (loadedState != null) {
|
||||
state = loadedState;
|
||||
}
|
||||
} catch (err) {
|
||||
// For some reason json can't be read.
|
||||
// No worries, we have defaults.
|
||||
}
|
||||
|
||||
var saveState = function (win) {
|
||||
if (!win.isMaximized() && !win.isMinimized() && win.isVisible()) {
|
||||
var position = win.getPosition();
|
||||
var size = win.getSize();
|
||||
state.x = position[0];
|
||||
state.y = position[1];
|
||||
state.width = size[0];
|
||||
state.height = size[1];
|
||||
}
|
||||
state.isMaximized = win.isMaximized();
|
||||
state.isMinimized = win.isMinimized();
|
||||
state.isHidden = !win.isMinimized() && !win.isVisible();
|
||||
userDataDir.write(stateStoreFile, state, { atomic: true });
|
||||
};
|
||||
|
||||
return {
|
||||
get x() { return state.x; },
|
||||
get y() { return state.y; },
|
||||
get width() { return state.width; },
|
||||
get height() { return state.height; },
|
||||
get isMaximized() { return state.isMaximized; },
|
||||
get isMinimized() { return state.isMinimized; },
|
||||
get isHidden() { return state.isHidden; },
|
||||
saveState: saveState
|
||||
};
|
||||
}
|
130
app/vendor/jasmine/boot.js
vendored
130
app/vendor/jasmine/boot.js
vendored
|
@ -1,130 +0,0 @@
|
|||
/**
|
||||
Starting with version 2.0, this file "boots" Jasmine, performing all of the necessary initialization before executing the loaded environment and all of a project's specs. This file should be loaded after `jasmine.js` and `jasmine_html.js`, but before any project source files or spec files are loaded. Thus this file can also be used to customize Jasmine for a project.
|
||||
|
||||
If a project is using Jasmine via the standalone distribution, this file can be customized directly. If a project is using Jasmine via the [Ruby gem][jasmine-gem], this file can be copied into the support directory via `jasmine copy_boot_js`. Other environments (e.g., Python) will have different mechanisms.
|
||||
|
||||
The location of `boot.js` can be specified and/or overridden in `jasmine.yml`.
|
||||
|
||||
[jasmine-gem]: http://github.com/pivotal/jasmine-gem
|
||||
*/
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* ## Require & Instantiate
|
||||
*
|
||||
* Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference.
|
||||
*/
|
||||
window.jasmine = jasmineRequire.core(jasmineRequire);
|
||||
|
||||
/**
|
||||
* Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference.
|
||||
*/
|
||||
jasmineRequire.html(jasmine);
|
||||
|
||||
/**
|
||||
* Create the Jasmine environment. This is used to run all specs in a project.
|
||||
*/
|
||||
var env = jasmine.getEnv();
|
||||
|
||||
/**
|
||||
* ## The Global Interface
|
||||
*
|
||||
* Build up the functions that will be exposed as the Jasmine public interface. A project can customize, rename or alias any of these functions as desired, provided the implementation remains unchanged.
|
||||
*/
|
||||
var jasmineInterface = jasmineRequire.interface(jasmine, env);
|
||||
|
||||
/**
|
||||
* Add all of the Jasmine global/public interface to the global scope, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`.
|
||||
*/
|
||||
extend(window, jasmineInterface);
|
||||
|
||||
/**
|
||||
* ## Runner Parameters
|
||||
*
|
||||
* More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface.
|
||||
*/
|
||||
|
||||
var queryString = new jasmine.QueryString({
|
||||
getWindowLocation: function() { return window.location; }
|
||||
});
|
||||
|
||||
var catchingExceptions = queryString.getParam("catch");
|
||||
env.catchExceptions(typeof catchingExceptions === "undefined" ? true : catchingExceptions);
|
||||
|
||||
var throwingExpectationFailures = queryString.getParam("throwFailures");
|
||||
env.throwOnExpectationFailure(throwingExpectationFailures);
|
||||
|
||||
var random = queryString.getParam("random");
|
||||
env.randomizeTests(random);
|
||||
|
||||
var seed = queryString.getParam("seed");
|
||||
if (seed) {
|
||||
env.seed(seed);
|
||||
}
|
||||
|
||||
/**
|
||||
* ## Reporters
|
||||
* The `HtmlReporter` builds all of the HTML UI for the runner page. This reporter paints the dots, stars, and x's for specs, as well as all spec names and all failures (if any).
|
||||
*/
|
||||
var htmlReporter = new jasmine.HtmlReporter({
|
||||
env: env,
|
||||
onRaiseExceptionsClick: function() { queryString.navigateWithNewParam("catch", !env.catchingExceptions()); },
|
||||
onThrowExpectationsClick: function() { queryString.navigateWithNewParam("throwFailures", !env.throwingExpectationFailures()); },
|
||||
onRandomClick: function() { queryString.navigateWithNewParam("random", !env.randomTests()); },
|
||||
addToExistingQueryString: function(key, value) { return queryString.fullStringWithNewParam(key, value); },
|
||||
getContainer: function() { return document.body; },
|
||||
createElement: function() { return document.createElement.apply(document, arguments); },
|
||||
createTextNode: function() { return document.createTextNode.apply(document, arguments); },
|
||||
timer: new jasmine.Timer()
|
||||
});
|
||||
|
||||
/**
|
||||
* The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results from JavaScript.
|
||||
*/
|
||||
env.addReporter(jasmineInterface.jsApiReporter);
|
||||
env.addReporter(htmlReporter);
|
||||
|
||||
/**
|
||||
* Filter which specs will be run by matching the start of the full name against the `spec` query param.
|
||||
*/
|
||||
var specFilter = new jasmine.HtmlSpecFilter({
|
||||
filterString: function() { return queryString.getParam("spec"); }
|
||||
});
|
||||
|
||||
env.specFilter = function(spec) {
|
||||
return specFilter.matches(spec.getFullName());
|
||||
};
|
||||
|
||||
/**
|
||||
* Setting up timing functions to be able to be overridden. Certain browsers (Safari, IE 8, phantomjs) require this hack.
|
||||
*/
|
||||
window.setTimeout = window.setTimeout;
|
||||
window.setInterval = window.setInterval;
|
||||
window.clearTimeout = window.clearTimeout;
|
||||
window.clearInterval = window.clearInterval;
|
||||
|
||||
/**
|
||||
* ## Execution
|
||||
*
|
||||
* Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded.
|
||||
*/
|
||||
var currentWindowOnload = window.onload;
|
||||
|
||||
window.onload = function() {
|
||||
if (currentWindowOnload) {
|
||||
currentWindowOnload();
|
||||
}
|
||||
htmlReporter.initialize();
|
||||
env.execute();
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function for readability above.
|
||||
*/
|
||||
function extend(destination, source) {
|
||||
for (var property in source) destination[property] = source[property];
|
||||
return destination;
|
||||
}
|
||||
|
||||
}());
|
473
app/vendor/jasmine/jasmine-html.js
vendored
473
app/vendor/jasmine/jasmine-html.js
vendored
|
@ -1,473 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2008-2015 Pivotal Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
jasmineRequire.html = function(j$) {
|
||||
j$.ResultsNode = jasmineRequire.ResultsNode();
|
||||
j$.HtmlReporter = jasmineRequire.HtmlReporter(j$);
|
||||
j$.QueryString = jasmineRequire.QueryString();
|
||||
j$.HtmlSpecFilter = jasmineRequire.HtmlSpecFilter();
|
||||
};
|
||||
|
||||
jasmineRequire.HtmlReporter = function(j$) {
|
||||
|
||||
var noopTimer = {
|
||||
start: function() {},
|
||||
elapsed: function() { return 0; }
|
||||
};
|
||||
|
||||
function HtmlReporter(options) {
|
||||
var env = options.env || {},
|
||||
getContainer = options.getContainer,
|
||||
createElement = options.createElement,
|
||||
createTextNode = options.createTextNode,
|
||||
onRaiseExceptionsClick = options.onRaiseExceptionsClick || function() {},
|
||||
onThrowExpectationsClick = options.onThrowExpectationsClick || function() {},
|
||||
onRandomClick = options.onRandomClick || function() {},
|
||||
addToExistingQueryString = options.addToExistingQueryString || defaultQueryString,
|
||||
timer = options.timer || noopTimer,
|
||||
results = [],
|
||||
specsExecuted = 0,
|
||||
failureCount = 0,
|
||||
pendingSpecCount = 0,
|
||||
htmlReporterMain,
|
||||
symbols,
|
||||
failedSuites = [];
|
||||
|
||||
this.initialize = function() {
|
||||
clearPrior();
|
||||
htmlReporterMain = createDom('div', {className: 'jasmine_html-reporter'},
|
||||
createDom('div', {className: 'jasmine-banner'},
|
||||
createDom('a', {className: 'jasmine-title', href: 'http://jasmine.github.io/', target: '_blank'}),
|
||||
createDom('span', {className: 'jasmine-version'}, j$.version)
|
||||
),
|
||||
createDom('ul', {className: 'jasmine-symbol-summary'}),
|
||||
createDom('div', {className: 'jasmine-alert'}),
|
||||
createDom('div', {className: 'jasmine-results'},
|
||||
createDom('div', {className: 'jasmine-failures'})
|
||||
)
|
||||
);
|
||||
getContainer().appendChild(htmlReporterMain);
|
||||
};
|
||||
|
||||
var totalSpecsDefined;
|
||||
this.jasmineStarted = function(options) {
|
||||
totalSpecsDefined = options.totalSpecsDefined || 0;
|
||||
timer.start();
|
||||
};
|
||||
|
||||
var summary = createDom('div', {className: 'jasmine-summary'});
|
||||
|
||||
var topResults = new j$.ResultsNode({}, '', null),
|
||||
currentParent = topResults;
|
||||
|
||||
this.suiteStarted = function(result) {
|
||||
currentParent.addChild(result, 'suite');
|
||||
currentParent = currentParent.last();
|
||||
};
|
||||
|
||||
this.suiteDone = function(result) {
|
||||
if (result.status == 'failed') {
|
||||
failedSuites.push(result);
|
||||
}
|
||||
|
||||
if (currentParent == topResults) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentParent = currentParent.parent;
|
||||
};
|
||||
|
||||
this.specStarted = function(result) {
|
||||
currentParent.addChild(result, 'spec');
|
||||
};
|
||||
|
||||
var failures = [];
|
||||
this.specDone = function(result) {
|
||||
if(noExpectations(result) && typeof console !== 'undefined' && typeof console.error !== 'undefined') {
|
||||
console.error('Spec \'' + result.fullName + '\' has no expectations.');
|
||||
}
|
||||
|
||||
if (result.status != 'disabled') {
|
||||
specsExecuted++;
|
||||
}
|
||||
|
||||
if (!symbols){
|
||||
symbols = find('.jasmine-symbol-summary');
|
||||
}
|
||||
|
||||
symbols.appendChild(createDom('li', {
|
||||
className: noExpectations(result) ? 'jasmine-empty' : 'jasmine-' + result.status,
|
||||
id: 'spec_' + result.id,
|
||||
title: result.fullName
|
||||
}
|
||||
));
|
||||
|
||||
if (result.status == 'failed') {
|
||||
failureCount++;
|
||||
|
||||
var failure =
|
||||
createDom('div', {className: 'jasmine-spec-detail jasmine-failed'},
|
||||
createDom('div', {className: 'jasmine-description'},
|
||||
createDom('a', {title: result.fullName, href: specHref(result)}, result.fullName)
|
||||
),
|
||||
createDom('div', {className: 'jasmine-messages'})
|
||||
);
|
||||
var messages = failure.childNodes[1];
|
||||
|
||||
for (var i = 0; i < result.failedExpectations.length; i++) {
|
||||
var expectation = result.failedExpectations[i];
|
||||
messages.appendChild(createDom('div', {className: 'jasmine-result-message'}, expectation.message));
|
||||
messages.appendChild(createDom('div', {className: 'jasmine-stack-trace'}, expectation.stack));
|
||||
}
|
||||
|
||||
failures.push(failure);
|
||||
}
|
||||
|
||||
if (result.status == 'pending') {
|
||||
pendingSpecCount++;
|
||||
}
|
||||
};
|
||||
|
||||
this.jasmineDone = function(doneResult) {
|
||||
var banner = find('.jasmine-banner');
|
||||
var alert = find('.jasmine-alert');
|
||||
var order = doneResult && doneResult.order;
|
||||
alert.appendChild(createDom('span', {className: 'jasmine-duration'}, 'finished in ' + timer.elapsed() / 1000 + 's'));
|
||||
|
||||
banner.appendChild(
|
||||
createDom('div', { className: 'jasmine-run-options' },
|
||||
createDom('span', { className: 'jasmine-trigger' }, 'Options'),
|
||||
createDom('div', { className: 'jasmine-payload' },
|
||||
createDom('div', { className: 'jasmine-exceptions' },
|
||||
createDom('input', {
|
||||
className: 'jasmine-raise',
|
||||
id: 'jasmine-raise-exceptions',
|
||||
type: 'checkbox'
|
||||
}),
|
||||
createDom('label', { className: 'jasmine-label', 'for': 'jasmine-raise-exceptions' }, 'raise exceptions')),
|
||||
createDom('div', { className: 'jasmine-throw-failures' },
|
||||
createDom('input', {
|
||||
className: 'jasmine-throw',
|
||||
id: 'jasmine-throw-failures',
|
||||
type: 'checkbox'
|
||||
}),
|
||||
createDom('label', { className: 'jasmine-label', 'for': 'jasmine-throw-failures' }, 'stop spec on expectation failure')),
|
||||
createDom('div', { className: 'jasmine-random-order' },
|
||||
createDom('input', {
|
||||
className: 'jasmine-random',
|
||||
id: 'jasmine-random-order',
|
||||
type: 'checkbox'
|
||||
}),
|
||||
createDom('label', { className: 'jasmine-label', 'for': 'jasmine-random-order' }, 'run tests in random order'))
|
||||
)
|
||||
));
|
||||
|
||||
var raiseCheckbox = find('#jasmine-raise-exceptions');
|
||||
|
||||
raiseCheckbox.checked = !env.catchingExceptions();
|
||||
raiseCheckbox.onclick = onRaiseExceptionsClick;
|
||||
|
||||
var throwCheckbox = find('#jasmine-throw-failures');
|
||||
throwCheckbox.checked = env.throwingExpectationFailures();
|
||||
throwCheckbox.onclick = onThrowExpectationsClick;
|
||||
|
||||
var randomCheckbox = find('#jasmine-random-order');
|
||||
randomCheckbox.checked = env.randomTests();
|
||||
randomCheckbox.onclick = onRandomClick;
|
||||
|
||||
var optionsMenu = find('.jasmine-run-options'),
|
||||
optionsTrigger = optionsMenu.querySelector('.jasmine-trigger'),
|
||||
optionsPayload = optionsMenu.querySelector('.jasmine-payload'),
|
||||
isOpen = /\bjasmine-open\b/;
|
||||
|
||||
optionsTrigger.onclick = function() {
|
||||
if (isOpen.test(optionsPayload.className)) {
|
||||
optionsPayload.className = optionsPayload.className.replace(isOpen, '');
|
||||
} else {
|
||||
optionsPayload.className += ' jasmine-open';
|
||||
}
|
||||
};
|
||||
|
||||
if (specsExecuted < totalSpecsDefined) {
|
||||
var skippedMessage = 'Ran ' + specsExecuted + ' of ' + totalSpecsDefined + ' specs - run all';
|
||||
alert.appendChild(
|
||||
createDom('span', {className: 'jasmine-bar jasmine-skipped'},
|
||||
createDom('a', {href: '?', title: 'Run all specs'}, skippedMessage)
|
||||
)
|
||||
);
|
||||
}
|
||||
var statusBarMessage = '';
|
||||
var statusBarClassName = 'jasmine-bar ';
|
||||
|
||||
if (totalSpecsDefined > 0) {
|
||||
statusBarMessage += pluralize('spec', specsExecuted) + ', ' + pluralize('failure', failureCount);
|
||||
if (pendingSpecCount) { statusBarMessage += ', ' + pluralize('pending spec', pendingSpecCount); }
|
||||
statusBarClassName += (failureCount > 0) ? 'jasmine-failed' : 'jasmine-passed';
|
||||
} else {
|
||||
statusBarClassName += 'jasmine-skipped';
|
||||
statusBarMessage += 'No specs found';
|
||||
}
|
||||
|
||||
var seedBar;
|
||||
if (order && order.random) {
|
||||
seedBar = createDom('span', {className: 'jasmine-seed-bar'},
|
||||
', randomized with seed ',
|
||||
createDom('a', {title: 'randomized with seed ' + order.seed, href: seedHref(order.seed)}, order.seed)
|
||||
);
|
||||
}
|
||||
|
||||
alert.appendChild(createDom('span', {className: statusBarClassName}, statusBarMessage, seedBar));
|
||||
|
||||
for(i = 0; i < failedSuites.length; i++) {
|
||||
var failedSuite = failedSuites[i];
|
||||
for(var j = 0; j < failedSuite.failedExpectations.length; j++) {
|
||||
var errorBarMessage = 'AfterAll ' + failedSuite.failedExpectations[j].message;
|
||||
var errorBarClassName = 'jasmine-bar jasmine-errored';
|
||||
alert.appendChild(createDom('span', {className: errorBarClassName}, errorBarMessage));
|
||||
}
|
||||
}
|
||||
|
||||
var results = find('.jasmine-results');
|
||||
results.appendChild(summary);
|
||||
|
||||
summaryList(topResults, summary);
|
||||
|
||||
function summaryList(resultsTree, domParent) {
|
||||
var specListNode;
|
||||
for (var i = 0; i < resultsTree.children.length; i++) {
|
||||
var resultNode = resultsTree.children[i];
|
||||
if (resultNode.type == 'suite') {
|
||||
var suiteListNode = createDom('ul', {className: 'jasmine-suite', id: 'suite-' + resultNode.result.id},
|
||||
createDom('li', {className: 'jasmine-suite-detail'},
|
||||
createDom('a', {href: specHref(resultNode.result)}, resultNode.result.description)
|
||||
)
|
||||
);
|
||||
|
||||
summaryList(resultNode, suiteListNode);
|
||||
domParent.appendChild(suiteListNode);
|
||||
}
|
||||
if (resultNode.type == 'spec') {
|
||||
if (domParent.getAttribute('class') != 'jasmine-specs') {
|
||||
specListNode = createDom('ul', {className: 'jasmine-specs'});
|
||||
domParent.appendChild(specListNode);
|
||||
}
|
||||
var specDescription = resultNode.result.description;
|
||||
if(noExpectations(resultNode.result)) {
|
||||
specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription;
|
||||
}
|
||||
if(resultNode.result.status === 'pending' && resultNode.result.pendingReason !== '') {
|
||||
specDescription = specDescription + ' PENDING WITH MESSAGE: ' + resultNode.result.pendingReason;
|
||||
}
|
||||
specListNode.appendChild(
|
||||
createDom('li', {
|
||||
className: 'jasmine-' + resultNode.result.status,
|
||||
id: 'spec-' + resultNode.result.id
|
||||
},
|
||||
createDom('a', {href: specHref(resultNode.result)}, specDescription)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (failures.length) {
|
||||
alert.appendChild(
|
||||
createDom('span', {className: 'jasmine-menu jasmine-bar jasmine-spec-list'},
|
||||
createDom('span', {}, 'Spec List | '),
|
||||
createDom('a', {className: 'jasmine-failures-menu', href: '#'}, 'Failures')));
|
||||
alert.appendChild(
|
||||
createDom('span', {className: 'jasmine-menu jasmine-bar jasmine-failure-list'},
|
||||
createDom('a', {className: 'jasmine-spec-list-menu', href: '#'}, 'Spec List'),
|
||||
createDom('span', {}, ' | Failures ')));
|
||||
|
||||
find('.jasmine-failures-menu').onclick = function() {
|
||||
setMenuModeTo('jasmine-failure-list');
|
||||
};
|
||||
find('.jasmine-spec-list-menu').onclick = function() {
|
||||
setMenuModeTo('jasmine-spec-list');
|
||||
};
|
||||
|
||||
setMenuModeTo('jasmine-failure-list');
|
||||
|
||||
var failureNode = find('.jasmine-failures');
|
||||
for (var i = 0; i < failures.length; i++) {
|
||||
failureNode.appendChild(failures[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return this;
|
||||
|
||||
function find(selector) {
|
||||
return getContainer().querySelector('.jasmine_html-reporter ' + selector);
|
||||
}
|
||||
|
||||
function clearPrior() {
|
||||
// return the reporter
|
||||
var oldReporter = find('');
|
||||
|
||||
if(oldReporter) {
|
||||
getContainer().removeChild(oldReporter);
|
||||
}
|
||||
}
|
||||
|
||||
function createDom(type, attrs, childrenVarArgs) {
|
||||
var el = createElement(type);
|
||||
|
||||
for (var i = 2; i < arguments.length; i++) {
|
||||
var child = arguments[i];
|
||||
|
||||
if (typeof child === 'string') {
|
||||
el.appendChild(createTextNode(child));
|
||||
} else {
|
||||
if (child) {
|
||||
el.appendChild(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var attr in attrs) {
|
||||
if (attr == 'className') {
|
||||
el[attr] = attrs[attr];
|
||||
} else {
|
||||
el.setAttribute(attr, attrs[attr]);
|
||||
}
|
||||
}
|
||||
|
||||
return el;
|
||||
}
|
||||
|
||||
function pluralize(singular, count) {
|
||||
var word = (count == 1 ? singular : singular + 's');
|
||||
|
||||
return '' + count + ' ' + word;
|
||||
}
|
||||
|
||||
function specHref(result) {
|
||||
return addToExistingQueryString('spec', result.fullName);
|
||||
}
|
||||
|
||||
function seedHref(seed) {
|
||||
return addToExistingQueryString('seed', seed);
|
||||
}
|
||||
|
||||
function defaultQueryString(key, value) {
|
||||
return '?' + key + '=' + value;
|
||||
}
|
||||
|
||||
function setMenuModeTo(mode) {
|
||||
htmlReporterMain.setAttribute('class', 'jasmine_html-reporter ' + mode);
|
||||
}
|
||||
|
||||
function noExpectations(result) {
|
||||
return (result.failedExpectations.length + result.passedExpectations.length) === 0 &&
|
||||
result.status === 'passed';
|
||||
}
|
||||
}
|
||||
|
||||
return HtmlReporter;
|
||||
};
|
||||
|
||||
jasmineRequire.HtmlSpecFilter = function() {
|
||||
function HtmlSpecFilter(options) {
|
||||
var filterString = options && options.filterString() && options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
|
||||
var filterPattern = new RegExp(filterString);
|
||||
|
||||
this.matches = function(specName) {
|
||||
return filterPattern.test(specName);
|
||||
};
|
||||
}
|
||||
|
||||
return HtmlSpecFilter;
|
||||
};
|
||||
|
||||
jasmineRequire.ResultsNode = function() {
|
||||
function ResultsNode(result, type, parent) {
|
||||
this.result = result;
|
||||
this.type = type;
|
||||
this.parent = parent;
|
||||
|
||||
this.children = [];
|
||||
|
||||
this.addChild = function(result, type) {
|
||||
this.children.push(new ResultsNode(result, type, this));
|
||||
};
|
||||
|
||||
this.last = function() {
|
||||
return this.children[this.children.length - 1];
|
||||
};
|
||||
}
|
||||
|
||||
return ResultsNode;
|
||||
};
|
||||
|
||||
jasmineRequire.QueryString = function() {
|
||||
function QueryString(options) {
|
||||
|
||||
this.navigateWithNewParam = function(key, value) {
|
||||
options.getWindowLocation().search = this.fullStringWithNewParam(key, value);
|
||||
};
|
||||
|
||||
this.fullStringWithNewParam = function(key, value) {
|
||||
var paramMap = queryStringToParamMap();
|
||||
paramMap[key] = value;
|
||||
return toQueryString(paramMap);
|
||||
};
|
||||
|
||||
this.getParam = function(key) {
|
||||
return queryStringToParamMap()[key];
|
||||
};
|
||||
|
||||
return this;
|
||||
|
||||
function toQueryString(paramMap) {
|
||||
var qStrPairs = [];
|
||||
for (var prop in paramMap) {
|
||||
qStrPairs.push(encodeURIComponent(prop) + '=' + encodeURIComponent(paramMap[prop]));
|
||||
}
|
||||
return '?' + qStrPairs.join('&');
|
||||
}
|
||||
|
||||
function queryStringToParamMap() {
|
||||
var paramStr = options.getWindowLocation().search.substring(1),
|
||||
params = [],
|
||||
paramMap = {};
|
||||
|
||||
if (paramStr.length > 0) {
|
||||
params = paramStr.split('&');
|
||||
for (var i = 0; i < params.length; i++) {
|
||||
var p = params[i].split('=');
|
||||
var value = decodeURIComponent(p[1]);
|
||||
if (value === 'true' || value === 'false') {
|
||||
value = JSON.parse(value);
|
||||
}
|
||||
paramMap[decodeURIComponent(p[0])] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return paramMap;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return QueryString;
|
||||
};
|
58
app/vendor/jasmine/jasmine.css
vendored
58
app/vendor/jasmine/jasmine.css
vendored
File diff suppressed because one or more lines are too long
3456
app/vendor/jasmine/jasmine.js
vendored
3456
app/vendor/jasmine/jasmine.js
vendored
File diff suppressed because it is too large
Load diff
46
appveyor.yml
46
appveyor.yml
|
@ -1,34 +1,26 @@
|
|||
platform:
|
||||
- x64
|
||||
build: off
|
||||
|
||||
os: unstable
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
skip_tags: true
|
||||
|
||||
environment:
|
||||
nodejs_version: "6.3.0"
|
||||
|
||||
cache:
|
||||
- node_modules
|
||||
- '%USERPROFILE%\.electron'
|
||||
- node_modules -> package.json
|
||||
|
||||
install:
|
||||
- ps: Install-Product node 6 x64
|
||||
- choco install nsis -version 3.01.0.20160420
|
||||
- SET PATH=C:/Program Files (x86)/NSIS;%PATH%
|
||||
- ps: Install-Product node $env:nodejs_version
|
||||
- npm install npm
|
||||
- .\node_modules\.bin\npm install
|
||||
|
||||
build_script:
|
||||
- ps: >-
|
||||
if (-not (Test-Path env:APPVEYOR_REPO_TAG_NAME)) { $env:APPVEYOR_REPO_TAG_NAME = '0.0.0' }
|
||||
(gc app\package.json) -replace '"version": "[0-9.]+"', ('"version": "'+$env:APPVEYOR_REPO_TAG_NAME+'"') | Set-Content app\package.json
|
||||
- npm run release
|
||||
|
||||
artifacts:
|
||||
- path: 'releases\*.exe'
|
||||
|
||||
deploy:
|
||||
- provider: GitHub
|
||||
artifact: /.*\.exe/
|
||||
auth_token:
|
||||
secure: rqUx+ZsMc/HqozfHhXVgjG9rd0Iqyg+cVNakbk7EXXEsQvYC/E77jMgJutWlbI6V
|
||||
draft: false
|
||||
prerelease: false
|
||||
on:
|
||||
appveyor_repo_tag: true # deploy on tag push only
|
||||
|
||||
test: off
|
||||
test_script:
|
||||
- node --version
|
||||
- .\node_modules\.bin\npm --version
|
||||
- .\node_modules\.bin\npm test
|
||||
- .\node_modules\.bin\npm run e2e
|
||||
|
|
BIN
build/icon.icns
Normal file
BIN
build/icon.icns
Normal file
Binary file not shown.
BIN
build/icon.ico
Normal file
BIN
build/icon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 361 KiB |
BIN
build/icons/512x512.png
Normal file
BIN
build/icons/512x512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
14
e2e/hello_world.e2e.js
Normal file
14
e2e/hello_world.e2e.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { expect } from 'chai';
|
||||
import testUtils from './utils';
|
||||
|
||||
describe('application launch', function () {
|
||||
|
||||
beforeEach(testUtils.beforeEach);
|
||||
afterEach(testUtils.afterEach);
|
||||
|
||||
it('shows hello world text on screen after launch', function () {
|
||||
return this.app.client.getText('#greet').then(function (text) {
|
||||
expect(text).to.equal('Hello World!');
|
||||
});
|
||||
});
|
||||
});
|
25
e2e/utils.js
Normal file
25
e2e/utils.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
import electron from 'electron';
|
||||
import { Application } from 'spectron';
|
||||
|
||||
var beforeEach = function () {
|
||||
this.timeout(10000);
|
||||
this.app = new Application({
|
||||
path: electron,
|
||||
args: ['.'],
|
||||
startTimeout: 10000,
|
||||
waitTimeout: 10000,
|
||||
});
|
||||
return this.app.start();
|
||||
};
|
||||
|
||||
var afterEach = function () {
|
||||
this.timeout(10000);
|
||||
if (this.app && this.app.isRunning()) {
|
||||
return this.app.stop();
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
beforeEach: beforeEach,
|
||||
afterEach: afterEach,
|
||||
};
|
|
@ -1,4 +1,5 @@
|
|||
'use strict';
|
||||
|
||||
require('./tasks/build/build');
|
||||
require('./tasks/release/release');
|
||||
require('./tasks/build_app');
|
||||
require('./tasks/build_tests');
|
||||
require('./tasks/start');
|
||||
|
|
78
package.json
78
package.json
|
@ -1,37 +1,57 @@
|
|||
{
|
||||
"devDependencies": {
|
||||
"asar": "^0.12.3",
|
||||
"electron": "1.4.13",
|
||||
"electron-rebuild": "^1.2.1",
|
||||
"fs-jetpack": "^0.10.5",
|
||||
"gulp": "^3.9.1",
|
||||
"gulp-less": "^3.1.0",
|
||||
"gulp-util": "^3.0.7",
|
||||
"q": "^1.4.1",
|
||||
"rollup": "^0.38.0",
|
||||
"tree-kill": "^1.1.0",
|
||||
"yargs": "^6.5.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"appdmg": "^0.4.5",
|
||||
"rcedit": "^0.7.0"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "cd app && npm install",
|
||||
"build": "gulp build",
|
||||
"release": "gulp release --env=production",
|
||||
"start": "node ./tasks/start",
|
||||
"test": "node ./tasks/start --env=test",
|
||||
"install-native": "node ./tasks/install_native_module",
|
||||
"build-asar": "gulp build:asar --env=production"
|
||||
},
|
||||
"name": "rocketchat",
|
||||
"productName": "Rocket.Chat+",
|
||||
"description": "Rocket.Chat Native Cross-Platform Desktop Application via Electron.",
|
||||
"version": "2.0.2",
|
||||
"author": "Rocket.Chat Support <support@rocket.chat>",
|
||||
"copyright": "© 2016, Rocket.Chat",
|
||||
"homepage": "http://example.com",
|
||||
"license": "MIT",
|
||||
"main": "app/background.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/RocketChat/Rocket.Chat.Electron.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"build": {
|
||||
"appId": "chat.rocket",
|
||||
"files": [
|
||||
"app/**/*",
|
||||
"node_modules/**/*",
|
||||
"package.json"
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "install-app-deps",
|
||||
"build": "gulp build",
|
||||
"prerelease": "gulp build --env=production",
|
||||
"release": "build",
|
||||
"start": "gulp start",
|
||||
"pretest": "gulp build-unit --env=test",
|
||||
"test": "electron-mocha app/specs.js.autogenerated --renderer --require source-map-support/register",
|
||||
"coverage": "npm test -- -R scripts/istanbul-reporter",
|
||||
"pree2e": "gulp build-e2e --env=test",
|
||||
"e2e": "mocha app/e2e.js.autogenerated --require source-map-support/register"
|
||||
},
|
||||
"dependencies": {
|
||||
"electron-notification-shim": "^1.1.0",
|
||||
"electron-toaster": "^2.0.2"
|
||||
"fs-jetpack": "^0.10.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^3.5.0",
|
||||
"electron": "^1.4.7",
|
||||
"electron-builder": "^8.6.0",
|
||||
"electron-mocha": "^3.0.0",
|
||||
"gulp": "^3.9.0",
|
||||
"gulp-batch": "^1.0.5",
|
||||
"gulp-less": "^3.0.3",
|
||||
"gulp-plumber": "^1.1.0",
|
||||
"gulp-util": "^3.0.6",
|
||||
"gulp-watch": "^4.3.5",
|
||||
"istanbul": "^0.4.3",
|
||||
"minimist": "^1.2.0",
|
||||
"mocha": "^3.0.2",
|
||||
"rollup": "^0.36.3",
|
||||
"rollup-plugin-istanbul": "^1.1.0",
|
||||
"source-map-support": "^0.4.2",
|
||||
"spectron": "^3.3.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
Package: {{name}}
|
||||
Version: {{version}}
|
||||
Maintainer: {{author}}
|
||||
Priority: optional
|
||||
Architecture: amd64
|
||||
Installed-Size: {{size}}
|
||||
Description: {{description}}
|
|
@ -1,11 +0,0 @@
|
|||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Encoding=UTF-8
|
||||
Name={{productName}}
|
||||
Comment={{description}}
|
||||
Exec=/opt/{{name}}/{{name}}
|
||||
Path=/opt/{{name}}/
|
||||
Icon=/opt/{{name}}/icon.png
|
||||
Terminal=false
|
||||
Categories=Network;InstantMessaging;Chat;
|
|
@ -1,40 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>{{productName}}</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>{{productName}}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>icon.icns</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>{{identifier}}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>{{productName}}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>{{build}}</string>
|
||||
<key>CFBundleVersionString</key>
|
||||
<string>{{build}}</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>{{version}}</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>{{version}}</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.8.0</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>AtomApplication</string>
|
||||
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
||||
<true/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>{{copyright}}</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>{{LSApplicationCategoryType}}</string>
|
||||
</dict>
|
||||
</plist>
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"title": "{{productName}}",
|
||||
"icon": "{{dmgIcon}}",
|
||||
"background": "{{dmgBackground}}",
|
||||
"icon-size": 128,
|
||||
"contents": [
|
||||
{ "x": 410, "y": 220, "type": "link", "path": "/Applications" },
|
||||
{ "x": 130, "y": 220, "type": "file", "path": "{{appPath}}" }
|
||||
]
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>{{productName}} Helper EH</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>{{productName}} Helper EH</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>{{identifier}}.helper.EH</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>{{productName}} Helper EH</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>DTSDKName</key>
|
||||
<string>macosx</string>
|
||||
<key>LSUIElement</key>
|
||||
<true/>
|
||||
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -1,22 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>{{productName}} Helper NP</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>{{productName}} Helper NP</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>{{identifier}}.helper.NP</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>{{productName}} Helper NP</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>DTSDKName</key>
|
||||
<string>macosx</string>
|
||||
<key>LSUIElement</key>
|
||||
<true/>
|
||||
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -1,18 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>{{identifier}}.helper</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>{{productName}} Helper</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>DTSDKName</key>
|
||||
<string>macosx</string>
|
||||
<key>LSUIElement</key>
|
||||
<true/>
|
||||
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -1,339 +0,0 @@
|
|||
; NSIS packaging/install script
|
||||
; Docs: http://nsis.sourceforge.net/Docs/Contents.html
|
||||
|
||||
; --------------------------------
|
||||
; Includes
|
||||
; --------------------------------
|
||||
|
||||
!include LogicLib.nsh
|
||||
!include nsDialogs.nsh
|
||||
!include FileFunc.nsh
|
||||
!insertmacro GetParameters
|
||||
|
||||
; --------------------------------
|
||||
; Variables
|
||||
; --------------------------------
|
||||
|
||||
!define dest "{{dest}}"
|
||||
!define src "{{src}}"
|
||||
!define name "{{name}}"
|
||||
!define productName "{{productName}}"
|
||||
!define author "{{author}}"
|
||||
!define version "{{version}}"
|
||||
!define icon "{{icon}}"
|
||||
!define setupIcon "{{setupIcon}}"
|
||||
!define banner "{{banner}}"
|
||||
|
||||
!define exec "{{productName}}.exe"
|
||||
|
||||
!define regkey "Software\${productName}"
|
||||
!define uninstkey "Software\Microsoft\Windows\CurrentVersion\Uninstall\${productName}"
|
||||
|
||||
!define uninstaller "uninstall.exe"
|
||||
|
||||
; --------------------------------
|
||||
; Installation
|
||||
; --------------------------------
|
||||
|
||||
Unicode true
|
||||
SetCompressor /SOLID lzma
|
||||
|
||||
Name "${productName}"
|
||||
Icon "${setupIcon}"
|
||||
OutFile "${dest}"
|
||||
InstallDir "$PROGRAMFILES\${productName}"
|
||||
InstallDirRegKey HKLM "${regkey}" ""
|
||||
|
||||
RequestExecutionLevel admin
|
||||
CRCCheck on
|
||||
SilentInstall normal
|
||||
|
||||
XPStyle on
|
||||
ShowInstDetails nevershow
|
||||
AutoCloseWindow false
|
||||
WindowIcon off
|
||||
|
||||
Caption "${productName} Setup"
|
||||
; Don't add sub-captions to title bar
|
||||
SubCaption 3 " "
|
||||
SubCaption 4 " "
|
||||
|
||||
; --------------------------------
|
||||
; Page layout
|
||||
; --------------------------------
|
||||
|
||||
Page custom welcome
|
||||
Page components
|
||||
Page directory
|
||||
Page instfiles
|
||||
Page custom finish finishEnd
|
||||
|
||||
; --------------------------------
|
||||
; Control variables
|
||||
; --------------------------------
|
||||
|
||||
Var Image
|
||||
Var ImageHandle
|
||||
|
||||
Var LaunchAppCheckbox
|
||||
Var LaunchAppCheckbox_State
|
||||
|
||||
; --------------------------------
|
||||
; Installer init
|
||||
; --------------------------------
|
||||
|
||||
Function .onInit
|
||||
|
||||
; --------------------------------
|
||||
; Checking previously installed version
|
||||
; --------------------------------
|
||||
|
||||
ReadRegStr $R0 HKLM \
|
||||
"Software\Microsoft\Windows\CurrentVersion\Uninstall\${productName}" \
|
||||
"UninstallString"
|
||||
StrCmp $R0 "" done
|
||||
|
||||
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION \
|
||||
"${productName} is already installed. $\n$\nClick `OK` to remove the \
|
||||
previous version or `Cancel` to cancel this upgrade." \
|
||||
IDOK uninst
|
||||
Abort
|
||||
; --------------------------------
|
||||
; Run the uninstaller
|
||||
; --------------------------------
|
||||
uninst:
|
||||
ClearErrors
|
||||
ExecWait '$R0 _?=$INSTDIR' ;Do not copy the uninstaller to a temp file
|
||||
|
||||
IfErrors no_remove_uninstaller done
|
||||
;You can either use Delete /REBOOTOK in the uninstaller or add some code
|
||||
;here to remove the uninstaller. Use a registry key to check
|
||||
;whether the user has chosen to uninstall. If you are using an uninstaller
|
||||
;components page, make sure all sections are uninstalled.
|
||||
no_remove_uninstaller:
|
||||
|
||||
done:
|
||||
; --------------------------------
|
||||
; End of uninstaller
|
||||
; --------------------------------
|
||||
|
||||
|
||||
; Set installer to silent if /silent switch was provided
|
||||
${GetParameters} $R0
|
||||
${GetOptionsS} $R0 "/silent" $0
|
||||
IfErrors +2 0
|
||||
SetSilent silent
|
||||
ClearErrors
|
||||
|
||||
; Extract banner image for welcome page
|
||||
InitPluginsDir
|
||||
ReserveFile "${banner}"
|
||||
File /oname=$PLUGINSDIR\banner.bmp "${banner}"
|
||||
|
||||
; Check if the application is currently running, show message if it is
|
||||
retryInstallation:
|
||||
FindWindow $0 "Chrome_WidgetWin_1" "${productName}"
|
||||
StrCmp $0 0 notRunning
|
||||
MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION "${productName} is currently running. Please close the application to continue." /SD IDCANCEL IDRETRY retryInstallation
|
||||
Abort
|
||||
notRunning:
|
||||
|
||||
FunctionEnd
|
||||
|
||||
; --------------------------------
|
||||
; Welcome page [custom]
|
||||
; --------------------------------
|
||||
|
||||
Function welcome
|
||||
|
||||
nsDialogs::Create 1018
|
||||
|
||||
${NSD_CreateLabel} 185 1u 210 100% "Welcome to ${productName} version ${version} installer.$\r$\n$\r$\nClick next to continue."
|
||||
|
||||
${NSD_CreateBitmap} 0 0 170 210 ""
|
||||
Pop $Image
|
||||
${NSD_SetImage} $Image $PLUGINSDIR\banner.bmp $ImageHandle
|
||||
|
||||
nsDialogs::Show
|
||||
|
||||
${NSD_FreeImage} $ImageHandle
|
||||
|
||||
FunctionEnd
|
||||
|
||||
; --------------------------------
|
||||
; Installation sections
|
||||
; --------------------------------
|
||||
|
||||
Section "${productName} Client"
|
||||
|
||||
; Make this section a requirement
|
||||
SectionIn RO
|
||||
|
||||
WriteRegStr HKLM "${regkey}" "Install_Dir" "$INSTDIR"
|
||||
WriteRegStr HKLM "${uninstkey}" "DisplayName" "${productName}"
|
||||
WriteRegStr HKLM "${uninstkey}" "DisplayIcon" '"$INSTDIR\icon.ico"'
|
||||
WriteRegStr HKLM "${uninstkey}" "UninstallString" '"$INSTDIR\${uninstaller}"'
|
||||
WriteRegStr HKLM "${uninstkey}" "Publisher" "${author}"
|
||||
WriteRegStr HKLM "${uninstkey}" "DisplayVersion" "${version}"
|
||||
|
||||
; Remove all application files copied by previous installation
|
||||
RMDir /r "$INSTDIR"
|
||||
|
||||
SetOutPath $INSTDIR
|
||||
|
||||
; Include all files from /build directory
|
||||
File /r "${src}\*"
|
||||
|
||||
WriteUninstaller "${uninstaller}"
|
||||
|
||||
SectionEnd
|
||||
|
||||
Section "Desktop shortcut"
|
||||
|
||||
; Create desktop shortcut
|
||||
CreateShortCut "$DESKTOP\${productName}.lnk" "$INSTDIR\${exec}" "" "$INSTDIR\icon.ico"
|
||||
|
||||
SectionEnd
|
||||
|
||||
Section "Autostart Entry"
|
||||
|
||||
; Create autostart entry
|
||||
CreateShortCut "$SMSTARTUP\${productName}.lnk" "$INSTDIR\${exec}" "" "$INSTDIR\icon.ico"
|
||||
|
||||
SectionEnd
|
||||
|
||||
Section "Start Menu Entry"
|
||||
|
||||
; Create start menu entry
|
||||
SetShellVarContext all
|
||||
CreateShortCut "$SMPROGRAMS\${productName}.lnk" "$INSTDIR\${exec}" "" "$INSTDIR\icon.ico"
|
||||
|
||||
SectionEnd
|
||||
|
||||
; --------------------------------
|
||||
; Finish page [custom]
|
||||
; --------------------------------
|
||||
|
||||
Function finish
|
||||
|
||||
nsDialogs::Create 1018
|
||||
|
||||
${NSD_CreateLabel} 185 1u 210 30u "${productName} installation successfully finished."
|
||||
|
||||
${NSD_CreateCheckbox} 185 35u 100% 10u "Launch ${productName}"
|
||||
Pop $LaunchAppCheckbox
|
||||
${NSD_SetState} $LaunchAppCheckbox ${BST_CHECKED}
|
||||
|
||||
${NSD_CreateBitmap} 0 0 170 210 ""
|
||||
Pop $Image
|
||||
${NSD_SetImage} $Image $PLUGINSDIR\banner.bmp $ImageHandle
|
||||
|
||||
nsDialogs::Show
|
||||
|
||||
${NSD_FreeImage} $ImageHandle
|
||||
|
||||
FunctionEnd
|
||||
|
||||
Function finishEnd
|
||||
; Save checkbox state on installer leave
|
||||
${NSD_GetState} $LaunchAppCheckbox $LaunchAppCheckbox_State
|
||||
|
||||
; Launch the app, if the box is checked
|
||||
${If} $LaunchAppCheckbox_State == ${BST_CHECKED}
|
||||
Exec "$INSTDIR\${exec}"
|
||||
${EndIf}
|
||||
FunctionEnd
|
||||
|
||||
; --------------------------------
|
||||
; Uninstaller
|
||||
; --------------------------------
|
||||
|
||||
ShowUninstDetails nevershow
|
||||
|
||||
UninstallCaption "Uninstall ${productName}"
|
||||
UninstallText "Don't like ${productName} anymore? Hit uninstall button."
|
||||
UninstallIcon "${icon}"
|
||||
|
||||
; --------------------------------
|
||||
; Page layout
|
||||
; --------------------------------
|
||||
|
||||
UninstPage custom un.confirm un.confirmOnLeave
|
||||
UninstPage instfiles
|
||||
|
||||
; --------------------------------
|
||||
; Control variables
|
||||
; --------------------------------
|
||||
|
||||
Var RemoveAppDataCheckbox
|
||||
Var RemoveAppDataCheckbox_State
|
||||
|
||||
; --------------------------------
|
||||
; Uninstaller init
|
||||
; --------------------------------
|
||||
|
||||
Function un.onInit
|
||||
|
||||
; Check if the application is currently running, show message if it is
|
||||
retryUninstall:
|
||||
FindWindow $0 "Chrome_WidgetWin_1" "${productName}"
|
||||
StrCmp $0 0 notRunning
|
||||
MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION "${productName} is currently running. Please close the application to continue." /SD IDCANCEL IDRETRY retryUninstall
|
||||
Abort
|
||||
notRunning:
|
||||
|
||||
FunctionEnd
|
||||
|
||||
; --------------------------------
|
||||
; Confirm page [custom]
|
||||
; --------------------------------
|
||||
|
||||
Function un.confirm
|
||||
|
||||
nsDialogs::Create 1018
|
||||
|
||||
${NSD_CreateLabel} 1u 1u 100% 24u "If you really want to remove ${productName} from your computer press uninstall button."
|
||||
|
||||
${NSD_CreateCheckbox} 1u 35u 100% 10u "Remove also my ${productName} personal data"
|
||||
Pop $RemoveAppDataCheckbox
|
||||
|
||||
nsDialogs::Show
|
||||
|
||||
FunctionEnd
|
||||
|
||||
Function un.confirmOnLeave
|
||||
|
||||
; Save checkbox state on page leave
|
||||
${NSD_GetState} $RemoveAppDataCheckbox $RemoveAppDataCheckbox_State
|
||||
|
||||
FunctionEnd
|
||||
|
||||
; --------------------------------
|
||||
; Uninstallation sections
|
||||
; --------------------------------
|
||||
|
||||
Section "Uninstall"
|
||||
|
||||
; Remove registry entries
|
||||
DeleteRegKey HKLM "${uninstkey}"
|
||||
DeleteRegKey HKLM "${regkey}"
|
||||
|
||||
; Remove desktop shortcut
|
||||
Delete "$DESKTOP\${productName}.lnk"
|
||||
|
||||
; Remove autostart entry
|
||||
Delete "$SMSTARTUP\${productName}.lnk"
|
||||
|
||||
; Remove start menu entry
|
||||
SetShellVarContext all
|
||||
Delete "$SMPROGRAMS\${productName}.lnk"
|
||||
|
||||
; Remove whole directory from installation directory
|
||||
RMDir /r "$INSTDIR"
|
||||
|
||||
; Remove also appData directory generated by your app if user checked this option
|
||||
${If} $RemoveAppDataCheckbox_State == ${BST_CHECKED}
|
||||
RMDir /r "$APPDATA\${productName}"
|
||||
${EndIf}
|
||||
|
||||
SectionEnd
|
26
scripts/istanbul-reporter.js
Normal file
26
scripts/istanbul-reporter.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
var istanbul = require('istanbul');
|
||||
|
||||
module.exports = function (runner, options) {
|
||||
mocha.reporters.Base.call(this, runner);
|
||||
|
||||
var reporterOpts = { dir: 'coverage' },
|
||||
reporters = ['text-summary', 'html'];
|
||||
|
||||
options = options || {};
|
||||
if (options.reporters) reporters = options.reporters.split(',');
|
||||
if (process.env.ISTANBUL_REPORTERS) reporters = process.env.ISTANBUL_REPORTERS.split(',');
|
||||
if (options.reportDir) reporterOpts.dir = options.reportDir;
|
||||
if (process.env.ISTANBUL_REPORT_DIR) reporterOpts.dir = process.env.ISTANBUL_REPORT_DIR;
|
||||
|
||||
runner.on('end', function(){
|
||||
var cov = global.__coverage__ || {},
|
||||
collector = new istanbul.Collector();
|
||||
|
||||
collector.add(cov);
|
||||
|
||||
reporters.forEach(function(reporter) {
|
||||
istanbul.Report.create(reporter, reporterOpts).writeReport(collector, true);
|
||||
});
|
||||
|
||||
});
|
||||
};
|
18
scripts/travis-build.sh
Executable file
18
scripts/travis-build.sh
Executable file
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
git clone https://github.com/creationix/nvm.git /tmp/.nvm
|
||||
source /tmp/.nvm/nvm.sh
|
||||
nvm install "$NODE_VERSION"
|
||||
nvm use --delete-prefix "$NODE_VERSION"
|
||||
|
||||
if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
|
||||
export DISPLAY=:99.0
|
||||
sh -e /etc/init.d/xvfb start
|
||||
sleep 3
|
||||
fi
|
||||
|
||||
node --version
|
||||
npm --version
|
||||
|
||||
npm install
|
||||
npm test & npm run e2e
|
56
src/background.js
Normal file
56
src/background.js
Normal file
|
@ -0,0 +1,56 @@
|
|||
// This is main process of Electron, started as first thing when your
|
||||
// app starts. This script is running through entire life of your application.
|
||||
// It doesn't have any windows which you can see on screen, but we can open
|
||||
// window from here.
|
||||
|
||||
import path from 'path';
|
||||
import url from 'url';
|
||||
import { app, Menu } from 'electron';
|
||||
import { devMenuTemplate } from './menu/dev_menu_template';
|
||||
import { editMenuTemplate } from './menu/edit_menu_template';
|
||||
import createWindow from './helpers/window';
|
||||
|
||||
// Special module holding environment variables which you declared
|
||||
// in config/env_xxx.json file.
|
||||
import env from './env';
|
||||
|
||||
var mainWindow;
|
||||
|
||||
var setApplicationMenu = function () {
|
||||
var menus = [editMenuTemplate];
|
||||
if (env.name !== 'production') {
|
||||
menus.push(devMenuTemplate);
|
||||
}
|
||||
Menu.setApplicationMenu(Menu.buildFromTemplate(menus));
|
||||
};
|
||||
|
||||
// Save userData in separate folders for each environment.
|
||||
// Thanks to this you can use production and development versions of the app
|
||||
// on same machine like those are two separate apps.
|
||||
if (env.name !== 'production') {
|
||||
var userDataPath = app.getPath('userData');
|
||||
app.setPath('userData', userDataPath + ' (' + env.name + ')');
|
||||
}
|
||||
|
||||
app.on('ready', function () {
|
||||
setApplicationMenu();
|
||||
|
||||
var mainWindow = createWindow('main', {
|
||||
width: 1000,
|
||||
height: 600
|
||||
});
|
||||
|
||||
mainWindow.loadURL(url.format({
|
||||
pathname: path.join(__dirname, 'app.html'),
|
||||
protocol: 'file:',
|
||||
slashes: true
|
||||
}));
|
||||
|
||||
if (env.name === 'development') {
|
||||
mainWindow.openDevTools();
|
||||
}
|
||||
});
|
||||
|
||||
app.on('window-all-closed', function () {
|
||||
app.quit();
|
||||
});
|
8
src/env.js
Normal file
8
src/env.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
// Simple wrapper exposing environment variables to rest of the code.
|
||||
|
||||
import jetpack from 'fs-jetpack';
|
||||
|
||||
// The variables have been written to `env.json` by the build process.
|
||||
var env = jetpack.cwd(__dirname).read('env.json', 'json');
|
||||
|
||||
export default env;
|
7
src/hello_world/hello_world.js
Normal file
7
src/hello_world/hello_world.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
export var greet = function () {
|
||||
return 'Hello World!';
|
||||
};
|
||||
|
||||
export var bye = function () {
|
||||
return 'See ya!';
|
||||
};
|
20
src/hello_world/hello_world.spec.js
Normal file
20
src/hello_world/hello_world.spec.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
import { expect } from 'chai';
|
||||
import { greet, bye } from './hello_world';
|
||||
import env from '../env';
|
||||
|
||||
describe("hello world", function () {
|
||||
|
||||
it("greets", function () {
|
||||
expect(greet()).to.equal('Hello World!');
|
||||
});
|
||||
|
||||
it("says goodbye", function () {
|
||||
expect(bye()).to.equal('See ya!');
|
||||
});
|
||||
|
||||
it("should load test environment variables", function () {
|
||||
expect(env.name).to.equal('test');
|
||||
expect(env.description).to.equal('Add here any environment specific stuff you like.');
|
||||
});
|
||||
|
||||
});
|
83
src/helpers/window.js
Normal file
83
src/helpers/window.js
Normal file
|
@ -0,0 +1,83 @@
|
|||
// This helper remembers the size and position of your windows (and restores
|
||||
// them in that place after app relaunch).
|
||||
// Can be used for more than one window, just construct many
|
||||
// instances of it and give each different name.
|
||||
|
||||
import { app, BrowserWindow, screen } from 'electron';
|
||||
import jetpack from 'fs-jetpack';
|
||||
|
||||
export default function (name, options) {
|
||||
|
||||
var userDataDir = jetpack.cwd(app.getPath('userData'));
|
||||
var stateStoreFile = 'window-state-' + name +'.json';
|
||||
var defaultSize = {
|
||||
width: options.width,
|
||||
height: options.height
|
||||
};
|
||||
var state = {};
|
||||
var win;
|
||||
|
||||
var restore = function () {
|
||||
var restoredState = {};
|
||||
try {
|
||||
restoredState = userDataDir.read(stateStoreFile, 'json');
|
||||
} catch (err) {
|
||||
// For some reason json can't be read (might be corrupted).
|
||||
// No worries, we have defaults.
|
||||
}
|
||||
return Object.assign({}, defaultSize, restoredState);
|
||||
};
|
||||
|
||||
var getCurrentPosition = function () {
|
||||
var position = win.getPosition();
|
||||
var size = win.getSize();
|
||||
return {
|
||||
x: position[0],
|
||||
y: position[1],
|
||||
width: size[0],
|
||||
height: size[1]
|
||||
};
|
||||
};
|
||||
|
||||
var windowWithinBounds = function (windowState, bounds) {
|
||||
return windowState.x >= bounds.x &&
|
||||
windowState.y >= bounds.y &&
|
||||
windowState.x + windowState.width <= bounds.x + bounds.width &&
|
||||
windowState.y + windowState.height <= bounds.y + bounds.height;
|
||||
};
|
||||
|
||||
var resetToDefaults = function (windowState) {
|
||||
var bounds = screen.getPrimaryDisplay().bounds;
|
||||
return Object.assign({}, defaultSize, {
|
||||
x: (bounds.width - defaultSize.width) / 2,
|
||||
y: (bounds.height - defaultSize.height) / 2
|
||||
});
|
||||
};
|
||||
|
||||
var ensureVisibleOnSomeDisplay = function (windowState) {
|
||||
var visible = screen.getAllDisplays().some(function (display) {
|
||||
return windowWithinBounds(windowState, display.bounds);
|
||||
});
|
||||
if (!visible) {
|
||||
// Window is partially or fully not visible now.
|
||||
// Reset it to safe defaults.
|
||||
return resetToDefaults(windowState);
|
||||
}
|
||||
return windowState;
|
||||
};
|
||||
|
||||
var saveState = function () {
|
||||
if (!win.isMinimized() && !win.isMaximized()) {
|
||||
Object.assign(state, getCurrentPosition());
|
||||
}
|
||||
userDataDir.write(stateStoreFile, state, { atomic: true });
|
||||
};
|
||||
|
||||
state = ensureVisibleOnSomeDisplay(restore());
|
||||
|
||||
win = new BrowserWindow(Object.assign({}, options, state));
|
||||
|
||||
win.on('close', saveState);
|
||||
|
||||
return win;
|
||||
}
|
24
src/menu/dev_menu_template.js
Normal file
24
src/menu/dev_menu_template.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { app, BrowserWindow } from 'electron';
|
||||
|
||||
export var devMenuTemplate = {
|
||||
label: 'Development',
|
||||
submenu: [{
|
||||
label: 'Reload',
|
||||
accelerator: 'CmdOrCtrl+R',
|
||||
click: function () {
|
||||
BrowserWindow.getFocusedWindow().webContents.reloadIgnoringCache();
|
||||
}
|
||||
},{
|
||||
label: 'Toggle DevTools',
|
||||
accelerator: 'Alt+CmdOrCtrl+I',
|
||||
click: function () {
|
||||
BrowserWindow.getFocusedWindow().toggleDevTools();
|
||||
}
|
||||
},{
|
||||
label: 'Quit',
|
||||
accelerator: 'CmdOrCtrl+Q',
|
||||
click: function () {
|
||||
app.quit();
|
||||
}
|
||||
}]
|
||||
};
|
12
src/menu/edit_menu_template.js
Normal file
12
src/menu/edit_menu_template.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
export var editMenuTemplate = {
|
||||
label: 'Edit',
|
||||
submenu: [
|
||||
{ label: "Undo", accelerator: "CmdOrCtrl+Z", selector: "undo:" },
|
||||
{ label: "Redo", accelerator: "Shift+CmdOrCtrl+Z", selector: "redo:" },
|
||||
{ type: "separator" },
|
||||
{ label: "Cut", accelerator: "CmdOrCtrl+X", selector: "cut:" },
|
||||
{ label: "Copy", accelerator: "CmdOrCtrl+C", selector: "copy:" },
|
||||
{ label: "Paste", accelerator: "CmdOrCtrl+V", selector: "paste:" },
|
||||
{ label: "Select All", accelerator: "CmdOrCtrl+A", selector: "selectAll:" }
|
||||
]
|
||||
};
|
|
@ -1,136 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var pathUtil = require('path');
|
||||
var Q = require('q');
|
||||
var gulp = require('gulp');
|
||||
var less = require('gulp-less');
|
||||
var jetpack = require('fs-jetpack');
|
||||
var asar = require('asar');
|
||||
|
||||
var bundle = require('./bundle');
|
||||
var generateSpecImportsFile = require('./generate_spec_imports');
|
||||
var utils = require('../utils');
|
||||
|
||||
var projectDir = jetpack;
|
||||
var srcDir = projectDir.cwd('./app');
|
||||
var destDir = projectDir.cwd('./build');
|
||||
|
||||
var paths = {
|
||||
copyFromAppDir: [
|
||||
'./branding/**',
|
||||
'./scripts/**',
|
||||
'./lib/**',
|
||||
'./spec.js',
|
||||
'./app.html',
|
||||
'./node_modules/**',
|
||||
'./vendor/**',
|
||||
'./images/**',
|
||||
'./icons/**',
|
||||
'./stylesheets/**/*.css',
|
||||
'./fonts/**',
|
||||
'./**/*.html',
|
||||
'./**/*.+(jpg|png|svg)'
|
||||
],
|
||||
};
|
||||
|
||||
// -------------------------------------
|
||||
// Tasks
|
||||
// -------------------------------------
|
||||
|
||||
gulp.task('clean', function () {
|
||||
return destDir.dirAsync('.', { empty: true });
|
||||
});
|
||||
|
||||
|
||||
var copyTask = function () {
|
||||
return projectDir.copyAsync('app', destDir.path(), {
|
||||
overwrite: true,
|
||||
matching: paths.copyFromAppDir
|
||||
});
|
||||
};
|
||||
gulp.task('copy', ['clean'], copyTask);
|
||||
gulp.task('copy-watch', copyTask);
|
||||
|
||||
|
||||
var bundleApplication = function () {
|
||||
return Q.all([
|
||||
bundle(srcDir.path('background.js'), destDir.path('background.js')),
|
||||
bundle(srcDir.path('servers.js'), destDir.path('servers.js')),
|
||||
bundle(srcDir.path('certificate.js'), destDir.path('certificate.js')),
|
||||
bundle(srcDir.path('app.js'), destDir.path('app.js')),
|
||||
]);
|
||||
};
|
||||
|
||||
var bundleSpecs = function () {
|
||||
return generateSpecImportsFile().then(function (specEntryPointPath) {
|
||||
return Q.all([
|
||||
bundle(srcDir.path('background.js'), destDir.path('background.js')),
|
||||
bundle(specEntryPointPath, destDir.path('spec.js')),
|
||||
]);
|
||||
});
|
||||
};
|
||||
|
||||
var bundleTask = function () {
|
||||
if (utils.getEnvName() === 'test') {
|
||||
return bundleSpecs();
|
||||
}
|
||||
return bundleApplication();
|
||||
};
|
||||
gulp.task('bundle', ['clean'], bundleTask);
|
||||
gulp.task('bundle-watch', bundleTask);
|
||||
|
||||
|
||||
var lessTask = function () {
|
||||
return gulp.src('app/stylesheets/main.less')
|
||||
.pipe(less())
|
||||
.pipe(gulp.dest(destDir.path('stylesheets')));
|
||||
};
|
||||
gulp.task('less', ['clean'], lessTask);
|
||||
gulp.task('less-watch', lessTask);
|
||||
|
||||
|
||||
gulp.task('finalize', ['clean'], function () {
|
||||
var manifest = srcDir.read('package.json', 'json');
|
||||
|
||||
// Add "dev" or "test" suffix to name, so Electron will write all data
|
||||
// like cookies and localStorage in separate places for each environment.
|
||||
switch (utils.getEnvName()) {
|
||||
case 'development':
|
||||
manifest.name += '-dev';
|
||||
manifest.productName += ' Dev';
|
||||
break;
|
||||
case 'test':
|
||||
manifest.name += '-test';
|
||||
manifest.productName += ' Test';
|
||||
break;
|
||||
}
|
||||
|
||||
// Copy environment variables to package.json file for easy use
|
||||
// in the running application. This is not official way of doing
|
||||
// things, but also isn't prohibited ;)
|
||||
manifest.env = projectDir.read('config/env_' + utils.getEnvName() + '.json', 'json');
|
||||
|
||||
destDir.write('package.json', manifest);
|
||||
});
|
||||
|
||||
|
||||
gulp.task('watch', function () {
|
||||
gulp.watch('app/**/*.js', ['bundle-watch']);
|
||||
gulp.watch(paths.copyFromAppDir, { cwd: 'app' }, ['copy-watch']);
|
||||
gulp.watch('app/**/*.less', ['less-watch']);
|
||||
});
|
||||
|
||||
var buildAsar = function () {
|
||||
var deferred = Q.defer();
|
||||
|
||||
asar.createPackage(destDir.cwd(), projectDir.path('app.asar'), function () {
|
||||
deferred.resolve();
|
||||
});
|
||||
deferred.resolve();
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
gulp.task('build:asar', ['build'], buildAsar);
|
||||
|
||||
gulp.task('build', ['bundle', 'less', 'copy', 'finalize']);
|
|
@ -1,52 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var pathUtil = require('path');
|
||||
var jetpack = require('fs-jetpack');
|
||||
var rollup = require('rollup');
|
||||
var Q = require('q');
|
||||
|
||||
var nodeBuiltInModules = ['assert', 'buffer', 'child_process', 'cluster',
|
||||
'console', 'constants', 'crypto', 'dgram', 'dns', 'domain', 'events',
|
||||
'fs', 'http', 'https', 'module', 'net', 'os', 'path', 'process', 'punycode',
|
||||
'querystring', 'readline', 'repl', 'stream', 'string_decoder', 'timers',
|
||||
'tls', 'tty', 'url', 'util', 'v8', 'vm', 'zlib'];
|
||||
|
||||
var electronBuiltInModules = ['electron'];
|
||||
|
||||
var npmModulesUsedInApp = function () {
|
||||
var appManifest = require('../../app/package.json');
|
||||
return Object.keys(appManifest.dependencies);
|
||||
};
|
||||
|
||||
var generateExternalModulesList = function () {
|
||||
return [].concat(nodeBuiltInModules, electronBuiltInModules, npmModulesUsedInApp());
|
||||
};
|
||||
|
||||
module.exports = function (src, dest) {
|
||||
var deferred = Q.defer();
|
||||
|
||||
rollup.rollup({
|
||||
entry: src,
|
||||
external: generateExternalModulesList(),
|
||||
}).then(function (bundle) {
|
||||
var jsFile = pathUtil.basename(dest);
|
||||
var result = bundle.generate({
|
||||
format: 'cjs',
|
||||
sourceMap: true,
|
||||
sourceMapFile: jsFile,
|
||||
});
|
||||
// Wrap code in self invoking function so the variables don't
|
||||
// pollute the global namespace.
|
||||
var isolatedCode = '(function () {' + result.code + '\n}());';
|
||||
return Q.all([
|
||||
jetpack.writeAsync(dest, isolatedCode + '\n//# sourceMappingURL=' + jsFile + '.map'),
|
||||
jetpack.writeAsync(dest + '.map', result.map.toString()),
|
||||
]);
|
||||
}).then(function () {
|
||||
deferred.resolve();
|
||||
}).catch(function (err) {
|
||||
console.error('Build: Error during rollup', err.stack);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
|
@ -1,28 +0,0 @@
|
|||
// Spec files are scattered through the whole project. Here we're searching
|
||||
// for them and generate one entry file which will run all the tests.
|
||||
|
||||
'use strict';
|
||||
|
||||
var jetpack = require('fs-jetpack');
|
||||
var srcDir = jetpack.cwd('app');
|
||||
|
||||
var fileName = 'spec.js';
|
||||
var fileBanner = "// This file is generated automatically.\n"
|
||||
+ "// All your modifications to it will be lost (so don't do it).\n";
|
||||
var whatToInclude = [
|
||||
'*.spec.js',
|
||||
'!node_modules/**',
|
||||
];
|
||||
|
||||
module.exports = function () {
|
||||
return srcDir.findAsync('.', { matching: whatToInclude }, 'relativePath')
|
||||
.then(function (specPaths) {
|
||||
var fileContent = specPaths.map(function (path) {
|
||||
return 'import "' + path + '";';
|
||||
}).join('\n');
|
||||
return srcDir.writeAsync(fileName, fileBanner + fileContent);
|
||||
})
|
||||
.then(function () {
|
||||
return srcDir.path(fileName);
|
||||
});
|
||||
};
|
53
tasks/build_app.js
Normal file
53
tasks/build_app.js
Normal file
|
@ -0,0 +1,53 @@
|
|||
'use strict';
|
||||
|
||||
var gulp = require('gulp');
|
||||
var less = require('gulp-less');
|
||||
var watch = require('gulp-watch');
|
||||
var batch = require('gulp-batch');
|
||||
var plumber = require('gulp-plumber');
|
||||
var jetpack = require('fs-jetpack');
|
||||
var bundle = require('./bundle');
|
||||
var utils = require('./utils');
|
||||
|
||||
var projectDir = jetpack;
|
||||
var srcDir = jetpack.cwd('./src');
|
||||
var destDir = jetpack.cwd('./app');
|
||||
|
||||
gulp.task('bundle', function () {
|
||||
return Promise.all([
|
||||
bundle(srcDir.path('background.js'), destDir.path('background.js')),
|
||||
bundle(srcDir.path('app.js'), destDir.path('app.js')),
|
||||
]);
|
||||
});
|
||||
|
||||
gulp.task('less', function () {
|
||||
return gulp.src(srcDir.path('stylesheets/main.less'))
|
||||
.pipe(plumber())
|
||||
.pipe(less())
|
||||
.pipe(gulp.dest(destDir.path('stylesheets')));
|
||||
});
|
||||
|
||||
gulp.task('environment', function () {
|
||||
var configFile = 'config/env_' + utils.getEnvName() + '.json';
|
||||
projectDir.copy(configFile, destDir.path('env.json'), { overwrite: true });
|
||||
});
|
||||
|
||||
gulp.task('watch', function () {
|
||||
var beepOnError = function (done) {
|
||||
return function (err) {
|
||||
if (err) {
|
||||
utils.beepSound();
|
||||
}
|
||||
done(err);
|
||||
};
|
||||
};
|
||||
|
||||
watch('src/**/*.js', batch(function (events, done) {
|
||||
gulp.start('bundle', beepOnError(done));
|
||||
}));
|
||||
watch('src/**/*.less', batch(function (events, done) {
|
||||
gulp.start('less', beepOnError(done));
|
||||
}));
|
||||
});
|
||||
|
||||
gulp.task('build', ['bundle', 'less', 'environment']);
|
51
tasks/build_tests.js
Normal file
51
tasks/build_tests.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
'use strict';
|
||||
|
||||
var gulp = require('gulp');
|
||||
var jetpack = require('fs-jetpack');
|
||||
var bundle = require('./bundle');
|
||||
var istanbul = require('rollup-plugin-istanbul');
|
||||
|
||||
// Spec files are scattered through the whole project. Here we're searching
|
||||
// for them and generate one entry file which will run all the tests.
|
||||
var generateEntryFile = function (dir, destFileName, filePattern) {
|
||||
var fileBanner = "// This file is generated automatically.\n"
|
||||
+ "// All modifications will be lost.\n";
|
||||
|
||||
return dir.findAsync('.', { matching: filePattern })
|
||||
.then(function (specPaths) {
|
||||
var fileContent = specPaths.map(function (path) {
|
||||
return 'import "./' + path.replace(/\\/g, '/') + '";';
|
||||
}).join('\n');
|
||||
return dir.writeAsync(destFileName, fileBanner + fileContent);
|
||||
})
|
||||
.then(function () {
|
||||
return dir.path(destFileName);
|
||||
});
|
||||
};
|
||||
|
||||
gulp.task('build-unit', ['environment'], function () {
|
||||
var srcDir = jetpack.cwd('src');
|
||||
var destDir = jetpack.cwd('app');
|
||||
|
||||
return generateEntryFile(srcDir, 'specs.js.autogenerated', '*.spec.js')
|
||||
.then(function (entryFilePath) {
|
||||
return bundle(entryFilePath, destDir.path('specs.js.autogenerated'), {
|
||||
rollupPlugins: [
|
||||
istanbul({
|
||||
exclude: ['**/*.spec.js', '**/specs.js.autogenerated'],
|
||||
sourceMap: true
|
||||
})
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('build-e2e', ['build'], function () {
|
||||
var srcDir = jetpack.cwd('e2e');
|
||||
var destDir = jetpack.cwd('app');
|
||||
|
||||
return generateEntryFile(srcDir, 'e2e.js.autogenerated', '*.e2e.js')
|
||||
.then(function (entryFilePath) {
|
||||
return bundle(entryFilePath, destDir.path('e2e.js.autogenerated'));
|
||||
});
|
||||
});
|
53
tasks/bundle.js
Normal file
53
tasks/bundle.js
Normal file
|
@ -0,0 +1,53 @@
|
|||
'use strict';
|
||||
|
||||
var path = require('path');
|
||||
var jetpack = require('fs-jetpack');
|
||||
var rollup = require('rollup').rollup;
|
||||
|
||||
var nodeBuiltInModules = ['assert', 'buffer', 'child_process', 'cluster',
|
||||
'console', 'constants', 'crypto', 'dgram', 'dns', 'domain', 'events',
|
||||
'fs', 'http', 'https', 'module', 'net', 'os', 'path', 'process', 'punycode',
|
||||
'querystring', 'readline', 'repl', 'stream', 'string_decoder', 'timers',
|
||||
'tls', 'tty', 'url', 'util', 'v8', 'vm', 'zlib'];
|
||||
|
||||
var electronBuiltInModules = ['electron'];
|
||||
|
||||
var generateExternalModulesList = function () {
|
||||
var appManifest = jetpack.read('./package.json', 'json');
|
||||
return [].concat(
|
||||
nodeBuiltInModules,
|
||||
electronBuiltInModules,
|
||||
Object.keys(appManifest.dependencies),
|
||||
Object.keys(appManifest.devDependencies)
|
||||
);
|
||||
};
|
||||
|
||||
var cached = {};
|
||||
|
||||
module.exports = function (src, dest, opts) {
|
||||
opts = opts || {};
|
||||
opts.rollupPlugins = opts.rollupPlugins || [];
|
||||
return rollup({
|
||||
entry: src,
|
||||
external: generateExternalModulesList(),
|
||||
cache: cached[src],
|
||||
plugins: opts.rollupPlugins,
|
||||
})
|
||||
.then(function (bundle) {
|
||||
cached[src] = bundle;
|
||||
|
||||
var jsFile = path.basename(dest);
|
||||
var result = bundle.generate({
|
||||
format: 'cjs',
|
||||
sourceMap: true,
|
||||
sourceMapFile: jsFile,
|
||||
});
|
||||
// Wrap code in self invoking function so the variables don't
|
||||
// pollute the global namespace.
|
||||
var isolatedCode = '(function () {' + result.code + '\n}());';
|
||||
return Promise.all([
|
||||
jetpack.writeAsync(dest, isolatedCode + '\n//# sourceMappingURL=' + jsFile + '.map'),
|
||||
jetpack.writeAsync(dest + '.map', result.map.toString()),
|
||||
]);
|
||||
});
|
||||
};
|
|
@ -1,95 +0,0 @@
|
|||
// Install native module from npm and compile it for Electron.
|
||||
// Usage: npm run install-native -- name_of_native_module
|
||||
|
||||
'use strict';
|
||||
|
||||
var childProcess = require('child_process');
|
||||
var Q = require('q');
|
||||
var appDir = require('fs-jetpack').cwd(__dirname, '../app');
|
||||
var argv = require('yargs').argv;
|
||||
var utils = require('./utils');
|
||||
|
||||
var ensureElectronRebuildInstalled = function () {
|
||||
var deferred = Q.defer();
|
||||
|
||||
try {
|
||||
// If require is successful it means module is already installed.
|
||||
require('electron-rebuild');
|
||||
deferred.resolve();
|
||||
} catch (err) {
|
||||
childProcess.spawn(utils.spawnablePath('npm'), [
|
||||
'install', '--save-dev', 'electron-rebuild'
|
||||
], {
|
||||
stdio: 'inherit'
|
||||
})
|
||||
.on('error', deferred.reject)
|
||||
.on('close', deferred.resolve);
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
var ensurePostinstallRunsElectronRebuild = function () {
|
||||
var postinstallScript = 'node ../tasks/rebuild_native_modules';
|
||||
|
||||
var appManifest = appDir.read('package.json', 'json');
|
||||
|
||||
if (typeof appManifest.scripts === 'undefined') {
|
||||
appManifest.scripts = {};
|
||||
}
|
||||
|
||||
// Let's do it 100% bulletproof and check if programmer didn't
|
||||
// pust some custom stuff into postinstall script already.
|
||||
if (typeof appManifest.scripts.postinstall === 'undefined') {
|
||||
appManifest.scripts.postinstall = postinstallScript;
|
||||
appDir.write('package.json', appManifest);
|
||||
} else if (appManifest.scripts.postinstall.indexOf(postinstallScript) === -1) {
|
||||
appManifest.scripts.postinstall += ' && ' + postinstallScript;
|
||||
appDir.write('package.json', appManifest);
|
||||
}
|
||||
|
||||
return Q();
|
||||
};
|
||||
|
||||
var installNativeModule = function () {
|
||||
var deferred = Q.defer();
|
||||
var moduleName = argv._[0];
|
||||
|
||||
if (!moduleName) {
|
||||
deferred.reject('Module name not specified! Correct usage is "npm run install-native -- name_of_native_module" (remember about space after "--").');
|
||||
} else {
|
||||
childProcess.spawn(utils.spawnablePath('npm'), [
|
||||
'install', '--save', moduleName
|
||||
], {
|
||||
cwd: appDir.cwd(),
|
||||
stdio: 'inherit'
|
||||
})
|
||||
.on('error', deferred.reject)
|
||||
.on('close', deferred.resolve);
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
var runRebuild = function () {
|
||||
var deferred = Q.defer();
|
||||
|
||||
childProcess.spawn(utils.spawnablePath('npm'), [
|
||||
'run', 'postinstall'
|
||||
], {
|
||||
cwd: appDir.cwd(),
|
||||
stdio: 'inherit'
|
||||
})
|
||||
.on('error', deferred.reject)
|
||||
.on('close', deferred.resolve);
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
ensureElectronRebuildInstalled()
|
||||
.then(ensurePostinstallRunsElectronRebuild)
|
||||
.then(installNativeModule)
|
||||
.then(runRebuild)
|
||||
.catch(function (err) {
|
||||
console.error(err);
|
||||
});
|
|
@ -1,33 +0,0 @@
|
|||
// Rebuilds native node modules for Electron.
|
||||
// More: https://github.com/atom/electron/blob/master/docs/tutorial/using-native-node-modules.md
|
||||
|
||||
'use strict';
|
||||
|
||||
var path = require('path');
|
||||
var Q = require('q');
|
||||
var electron = require('electron');
|
||||
var electronPackage = require('electron/package.json');
|
||||
var rebuild = require('electron-rebuild');
|
||||
|
||||
var pathToElectronNativeModules = path.join(__dirname, '../app/node_modules');
|
||||
|
||||
rebuild.shouldRebuildNativeModules(electron)
|
||||
.then(function (shouldBuild) {
|
||||
if (!shouldBuild) {
|
||||
return true;
|
||||
}
|
||||
|
||||
console.log('Rebuilding native modules for Electron...');
|
||||
|
||||
return rebuild.installNodeHeaders(electronPackage.version)
|
||||
.then(function () {
|
||||
return rebuild.rebuildNativeModules(electronPackage.version, pathToElectronNativeModules);
|
||||
});
|
||||
})
|
||||
.then(function () {
|
||||
console.log('Rebuilding complete.');
|
||||
})
|
||||
.catch(function (err) {
|
||||
console.error("Rebuilding error!");
|
||||
console.error(err);
|
||||
});
|
|
@ -1,160 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var Q = require('q');
|
||||
var gulpUtil = require('gulp-util');
|
||||
var childProcess = require('child_process');
|
||||
var jetpack = require('fs-jetpack');
|
||||
var asar = require('asar');
|
||||
var utils = require('../utils');
|
||||
|
||||
var projectDir;
|
||||
var releasesDir;
|
||||
var packName;
|
||||
var packDir;
|
||||
var tmpDir;
|
||||
var readyAppDir;
|
||||
var manifest;
|
||||
|
||||
var init = function () {
|
||||
projectDir = jetpack;
|
||||
tmpDir = projectDir.dir('./tmp', { empty: true });
|
||||
releasesDir = projectDir.dir('./releases');
|
||||
manifest = projectDir.read('app/package.json', 'json');
|
||||
packName = utils.getReleasePackageName(manifest);
|
||||
packDir = tmpDir.dir(packName);
|
||||
readyAppDir = packDir.cwd('opt', manifest.name);
|
||||
|
||||
return new Q();
|
||||
};
|
||||
|
||||
var copyRuntime = function () {
|
||||
return projectDir.copyAsync('node_modules/electron/dist', readyAppDir.path(), { overwrite: true });
|
||||
};
|
||||
|
||||
var packageBuiltApp = function () {
|
||||
var deferred = Q.defer();
|
||||
|
||||
asar.createPackage(projectDir.path('build'), readyAppDir.path('resources/app.asar'), function () {
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
var finalize = function () {
|
||||
// Create .desktop file from the template
|
||||
var desktop = projectDir.read('resources/linux/app.desktop');
|
||||
desktop = utils.replace(desktop, {
|
||||
name: manifest.name,
|
||||
productName: manifest.productName,
|
||||
description: manifest.description,
|
||||
version: manifest.version,
|
||||
author: manifest.author
|
||||
});
|
||||
packDir.write('usr/share/applications/' + manifest.name + '.desktop', desktop);
|
||||
|
||||
// Copy icon
|
||||
projectDir.copy('app/images/linux/icon.png', readyAppDir.path('icon.png'));
|
||||
|
||||
// Copy dictionaries
|
||||
projectDir.copy('dictionaries', readyAppDir.path('resources/dictionaries'));
|
||||
|
||||
return new Q();
|
||||
};
|
||||
|
||||
var renameApp = function () {
|
||||
return readyAppDir.renameAsync('electron', manifest.name);
|
||||
};
|
||||
|
||||
var packToDebFile = function () {
|
||||
var deferred = Q.defer();
|
||||
|
||||
var debFileName = packName + '.deb';
|
||||
var debPath = releasesDir.path(debFileName);
|
||||
|
||||
gulpUtil.log('Creating DEB package... (' + debFileName + ')');
|
||||
|
||||
// Counting size of the app in KiB
|
||||
var appSize = Math.round(readyAppDir.inspectTree('.').size / 1024);
|
||||
|
||||
// Preparing debian control file
|
||||
var control = projectDir.read('resources/linux/DEBIAN/control');
|
||||
control = utils.replace(control, {
|
||||
name: manifest.name,
|
||||
description: manifest.description,
|
||||
version: manifest.version,
|
||||
author: manifest.author,
|
||||
size: appSize
|
||||
});
|
||||
packDir.write('DEBIAN/control', control);
|
||||
|
||||
// Build the package...
|
||||
childProcess.exec('fakeroot dpkg-deb -Zxz --build ' + packDir.path().replace(/\s/g, '\\ ') + ' ' + debPath.replace(/\s/g, '\\ '),
|
||||
function (error, stdout, stderr) {
|
||||
if (error || stderr) {
|
||||
console.log('ERROR while building DEB package:');
|
||||
console.log(error);
|
||||
console.log(stderr);
|
||||
} else {
|
||||
gulpUtil.log('DEB package ready!', debPath);
|
||||
}
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
var packToRpmFile = function () {
|
||||
var deferred = Q.defer();
|
||||
|
||||
var rpmFileName = packName + '.rpm';
|
||||
var rpmPath = releasesDir.path(rpmFileName);
|
||||
|
||||
gulpUtil.log('Creating RPM package... (' + rpmFileName + ')');
|
||||
|
||||
// Preparing RPM Spec file
|
||||
var spec = projectDir.read('resources/linux/RHEL/app.spec');
|
||||
spec = utils.replace(spec, {
|
||||
name: manifest.name,
|
||||
description: manifest.description,
|
||||
version: process.env.TRAVIS_TAG || process.env.APPVEYOR_REPO_TAG_NAME || manifest.version,
|
||||
author: manifest.author
|
||||
});
|
||||
tmpDir.write('SPECS/app.spec', spec);
|
||||
|
||||
// Build the package...
|
||||
childProcess.exec('fakeroot rpmbuild --quiet -D "_topdir `pwd`/tmp" -D "_builddir ' + packDir.path() + '" -bb ' + tmpDir.path('SPECS/app.spec').replace(/\s/g, '\\ '),
|
||||
function (error, stdout, stderr) {
|
||||
if (error || stderr) {
|
||||
console.log('ERROR while building RPM package:');
|
||||
console.log(error);
|
||||
console.log(stderr);
|
||||
} else {
|
||||
// Copy to release directory
|
||||
tmpDir.copy('RPMS/x86_64', releasesDir.path(), {
|
||||
matching: '*.rpm',
|
||||
overwrite: true
|
||||
});
|
||||
gulpUtil.log('RPM package ready!', rpmPath);
|
||||
}
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
var cleanClutter = function () {
|
||||
return tmpDir.removeAsync('.');
|
||||
};
|
||||
|
||||
module.exports = function () {
|
||||
return init()
|
||||
.then(copyRuntime)
|
||||
.then(packageBuiltApp)
|
||||
.then(finalize)
|
||||
.then(renameApp)
|
||||
.then(packToDebFile)
|
||||
.then(packToRpmFile)
|
||||
.then(cleanClutter)
|
||||
.catch(console.error);
|
||||
};
|
|
@ -1,196 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var Q = require('q');
|
||||
var gulpUtil = require('gulp-util');
|
||||
var jetpack = require('fs-jetpack');
|
||||
var asar = require('asar');
|
||||
var utils = require('../utils');
|
||||
var child_process = require('child_process');
|
||||
|
||||
var projectDir;
|
||||
var releasesDir;
|
||||
var tmpDir;
|
||||
var finalAppDir;
|
||||
var manifest;
|
||||
|
||||
var init = function () {
|
||||
projectDir = jetpack;
|
||||
tmpDir = projectDir.dir('./tmp', { empty: true });
|
||||
releasesDir = projectDir.dir('./releases');
|
||||
manifest = projectDir.read('app/package.json', 'json');
|
||||
finalAppDir = tmpDir.cwd(manifest.productName + '.app');
|
||||
|
||||
return new Q();
|
||||
};
|
||||
|
||||
var copyRuntime = function () {
|
||||
return projectDir.copyAsync('node_modules/electron/dist/Electron.app', finalAppDir.path());
|
||||
};
|
||||
|
||||
var cleanupRuntime = function () {
|
||||
finalAppDir.remove('Contents/Resources/default_app');
|
||||
finalAppDir.remove('Contents/Resources/atom.icns');
|
||||
return new Q();
|
||||
};
|
||||
|
||||
var packageBuiltApp = function () {
|
||||
var deferred = Q.defer();
|
||||
|
||||
asar.createPackage(projectDir.path('build'), finalAppDir.path('Contents/Resources/app.asar'), function () {
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
var finalize = function () {
|
||||
// Prepare main Info.plist
|
||||
var info = projectDir.read('resources/osx/Info.plist');
|
||||
info = utils.replace(info, {
|
||||
productName: manifest.productName,
|
||||
identifier: manifest.identifier,
|
||||
version: manifest.version,
|
||||
build: manifest.build,
|
||||
copyright: manifest.copyright,
|
||||
LSApplicationCategoryType: manifest.LSApplicationCategoryType
|
||||
});
|
||||
finalAppDir.write('Contents/Info.plist', info);
|
||||
|
||||
// Prepare Info.plist of Helper apps
|
||||
[' EH', ' NP', ''].forEach(function (helper_suffix) {
|
||||
info = projectDir.read('resources/osx/helper_apps/Info' + helper_suffix + '.plist');
|
||||
info = utils.replace(info, {
|
||||
productName: manifest.productName,
|
||||
identifier: manifest.identifier
|
||||
});
|
||||
finalAppDir.write('Contents/Frameworks/Electron Helper' + helper_suffix + '.app/Contents/Info.plist', info);
|
||||
});
|
||||
|
||||
// Copy icon
|
||||
projectDir.copy('app/images/osx/icon.icns', finalAppDir.path('Contents/Resources/icon.icns'));
|
||||
|
||||
// Copy dictionaries
|
||||
projectDir.copy('dictionaries', finalAppDir.path('Contents/Resources/dictionaries'));
|
||||
|
||||
return new Q();
|
||||
};
|
||||
|
||||
var renameApp = function () {
|
||||
// Rename helpers
|
||||
[' Helper EH', ' Helper NP', ' Helper'].forEach(function (helper_suffix) {
|
||||
finalAppDir.rename('Contents/Frameworks/Electron' + helper_suffix + '.app/Contents/MacOS/Electron' + helper_suffix, manifest.productName + helper_suffix );
|
||||
finalAppDir.rename('Contents/Frameworks/Electron' + helper_suffix + '.app', manifest.productName + helper_suffix + '.app');
|
||||
});
|
||||
// Rename application
|
||||
finalAppDir.rename('Contents/MacOS/Electron', manifest.productName);
|
||||
return new Q();
|
||||
};
|
||||
|
||||
var signApp = function () {
|
||||
var identity = utils.getSigningId(manifest);
|
||||
var MASIdentity = utils.getMASSigningId(manifest);
|
||||
var MASInstallerIdentity = utils.getMASInstallerSigningId(manifest);
|
||||
|
||||
if (utils.releaseForMAS()) {
|
||||
if (!MASIdentity || !MASInstallerIdentity) {
|
||||
gulpUtil.log('--mas-sign and --mas-installer-sign are required to release for Mac App Store!');
|
||||
process.exit(0);
|
||||
}
|
||||
var cmds = [
|
||||
'codesign --deep -f -s "' + MASIdentity + '" --entitlements resources/osx/child.plist -v "' + finalAppDir.path() + '/Contents/Frameworks/Electron Framework.framework/Versions/A/Libraries/libffmpeg.dylib"',
|
||||
'codesign --deep -f -s "' + MASIdentity + '" --entitlements resources/osx/child.plist -v "' + finalAppDir.path() + '/Contents/Frameworks/Electron Framework.framework/Versions/A/Libraries/libnode.dylib"',
|
||||
'codesign --deep -f -s "' + MASIdentity + '" --entitlements resources/osx/child.plist -v "' + finalAppDir.path() + '/Contents/Frameworks/Electron Framework.framework/Versions/A"',
|
||||
'codesign --deep -f -s "' + MASIdentity + '" --entitlements resources/osx/child.plist -v "' + finalAppDir.path() + '/Contents/Frameworks/' + manifest.productName + ' Helper.app/"',
|
||||
'codesign --deep -f -s "' + MASIdentity + '" --entitlements resources/osx/child.plist -v "' + finalAppDir.path() + '/Contents/Frameworks/' + manifest.productName + ' Helper EH.app/"',
|
||||
'codesign --deep -f -s "' + MASIdentity + '" --entitlements resources/osx/child.plist -v "' + finalAppDir.path() + '/Contents/Frameworks/' + manifest.productName + ' Helper NP.app/"'
|
||||
];
|
||||
|
||||
if (finalAppDir.exists('Contents/Frameworks/Squirrel.framework/Versions/A')) {
|
||||
// # Signing a non-MAS build.
|
||||
cmds.push('codesign --deep -f -s "' + MASIdentity + '" --entitlements resources/osx/child.plist "' + finalAppDir.path() + '/Contents/Frameworks/Mantle.framework/Versions/A"');
|
||||
cmds.push('codesign --deep -f -s "' + MASIdentity + '" --entitlements resources/osx/child.plist "' + finalAppDir.path() + '/Contents/Frameworks/ReactiveCocoa.framework/Versions/A"');
|
||||
cmds.push('codesign --deep -f -s "' + MASIdentity + '" --entitlements resources/osx/child.plist "' + finalAppDir.path() + '/Contents/Frameworks/Squirrel.framework/Versions/A"');
|
||||
}
|
||||
|
||||
cmds.push('codesign -f -s "' + MASIdentity + '" --entitlements resources/osx/parent.plist -v "' + finalAppDir.path() + '"');
|
||||
|
||||
cmds.push('productbuild --component "' + finalAppDir.path() + '" /Applications --sign "' + MASInstallerIdentity + '" "' + releasesDir.path(manifest.productName + '.pkg') + '"');
|
||||
|
||||
var result = new Q();
|
||||
cmds.forEach(function (cmd) {
|
||||
result = result.then(function(result) {
|
||||
gulpUtil.log('Signing with:', cmd);
|
||||
return Q.nfcall(child_process.exec, cmd);
|
||||
});
|
||||
});
|
||||
result = result.then(function(result) {
|
||||
return new Q();
|
||||
});
|
||||
return result;
|
||||
|
||||
} else if (identity) {
|
||||
var cmd = 'codesign --deep --force --sign "' + identity + '" "' + finalAppDir.path() + '"';
|
||||
gulpUtil.log('Signing with:', cmd);
|
||||
return Q.nfcall(child_process.exec, cmd);
|
||||
} else {
|
||||
return new Q();
|
||||
}
|
||||
};
|
||||
|
||||
var packToDmgFile = function () {
|
||||
if (utils.releaseForMAS()) {
|
||||
return new Q();
|
||||
}
|
||||
|
||||
var deferred = Q.defer();
|
||||
|
||||
var appdmg = require('appdmg');
|
||||
var dmgName = utils.getReleasePackageName(manifest) + '.dmg';
|
||||
|
||||
// Prepare appdmg config
|
||||
var dmgManifest = projectDir.read('resources/osx/appdmg.json');
|
||||
dmgManifest = utils.replace(dmgManifest, {
|
||||
productName: manifest.productName,
|
||||
appPath: finalAppDir.path(),
|
||||
dmgIcon: projectDir.path('app/images/osx/dmg-icon.icns'),
|
||||
dmgBackground: projectDir.path('app/images/osx/dmg-background.png')
|
||||
});
|
||||
tmpDir.write('appdmg.json', dmgManifest);
|
||||
|
||||
// Delete DMG file with this name if already exists
|
||||
releasesDir.remove(dmgName);
|
||||
|
||||
gulpUtil.log('Packaging to DMG file... (' + dmgName + ')');
|
||||
|
||||
var readyDmgPath = releasesDir.path(dmgName);
|
||||
appdmg({
|
||||
source: tmpDir.path('appdmg.json'),
|
||||
target: readyDmgPath
|
||||
})
|
||||
.on('error', function (err) {
|
||||
console.error(err);
|
||||
})
|
||||
.on('finish', function () {
|
||||
gulpUtil.log('DMG file ready!', readyDmgPath);
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
var cleanClutter = function () {
|
||||
return tmpDir.removeAsync('.');
|
||||
};
|
||||
|
||||
module.exports = function () {
|
||||
return init()
|
||||
.then(copyRuntime)
|
||||
.then(cleanupRuntime)
|
||||
.then(packageBuiltApp)
|
||||
.then(finalize)
|
||||
.then(renameApp)
|
||||
.then(signApp)
|
||||
.then(packToDmgFile)
|
||||
.then(cleanClutter)
|
||||
.catch(console.error);
|
||||
};
|
|
@ -1,14 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var gulp = require('gulp');
|
||||
var utils = require('../utils');
|
||||
|
||||
var releaseForOs = {
|
||||
osx: require('./osx'),
|
||||
linux: require('./linux'),
|
||||
windows: require('./windows'),
|
||||
};
|
||||
|
||||
gulp.task('release', ['build'], function () {
|
||||
return releaseForOs[utils.os()]();
|
||||
});
|
|
@ -1,133 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var Q = require('q');
|
||||
var gulpUtil = require('gulp-util');
|
||||
var childProcess = require('child_process');
|
||||
var jetpack = require('fs-jetpack');
|
||||
var asar = require('asar');
|
||||
var utils = require('../utils');
|
||||
|
||||
var projectDir;
|
||||
var tmpDir;
|
||||
var releasesDir;
|
||||
var readyAppDir;
|
||||
var manifest;
|
||||
|
||||
var init = function () {
|
||||
projectDir = jetpack;
|
||||
tmpDir = projectDir.dir('./tmp', { empty: true });
|
||||
releasesDir = projectDir.dir('./releases');
|
||||
manifest = projectDir.read('app/package.json', 'json');
|
||||
readyAppDir = tmpDir.cwd(manifest.name);
|
||||
|
||||
return new Q();
|
||||
};
|
||||
|
||||
var copyRuntime = function () {
|
||||
return projectDir.copyAsync('node_modules/electron/dist', readyAppDir.path(), { overwrite: true });
|
||||
};
|
||||
|
||||
var cleanupRuntime = function () {
|
||||
return readyAppDir.removeAsync('resources/default_app');
|
||||
};
|
||||
|
||||
var packageBuiltApp = function () {
|
||||
var deferred = Q.defer();
|
||||
|
||||
asar.createPackage(projectDir.path('build'), readyAppDir.path('resources/app.asar'), function () {
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
var finalize = function () {
|
||||
var deferred = Q.defer();
|
||||
|
||||
projectDir.copy('app/images/windows/icon.ico', readyAppDir.path('icon.ico'));
|
||||
|
||||
// Replace Electron icon for your own.
|
||||
var rcedit = require('rcedit');
|
||||
rcedit(readyAppDir.path('electron.exe'), {
|
||||
'icon': projectDir.path('app/images/windows/icon.ico'),
|
||||
'version-string': {
|
||||
'ProductName': manifest.productName,
|
||||
'FileDescription': manifest.description,
|
||||
'ProductVersion': manifest.version,
|
||||
'CompanyName': manifest.author, // it might be better to add another field to package.json for this
|
||||
'LegalCopyright': manifest.copyright,
|
||||
'OriginalFilename': manifest.productName + '.exe'
|
||||
}
|
||||
}, function (err) {
|
||||
if (!err) {
|
||||
deferred.resolve();
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
var renameApp = function () {
|
||||
return readyAppDir.renameAsync('electron.exe', manifest.productName + '.exe');
|
||||
};
|
||||
|
||||
var createInstaller = function () {
|
||||
var deferred = Q.defer();
|
||||
|
||||
var finalPackageName = utils.getReleasePackageName(manifest) + '.exe';
|
||||
var installScript = projectDir.read('resources/windows/installer.nsi');
|
||||
|
||||
installScript = utils.replace(installScript, {
|
||||
name: manifest.name,
|
||||
productName: manifest.productName,
|
||||
author: manifest.author,
|
||||
version: manifest.version,
|
||||
src: readyAppDir.path(),
|
||||
dest: releasesDir.path(finalPackageName),
|
||||
icon: readyAppDir.path('icon.ico'),
|
||||
setupIcon: projectDir.path('app/images/windows/setup-icon.ico'),
|
||||
banner: projectDir.path('app/images/windows/setup-banner.bmp'),
|
||||
});
|
||||
tmpDir.write('installer.nsi', installScript);
|
||||
|
||||
gulpUtil.log('Building installer with NSIS... (' + finalPackageName + ')');
|
||||
|
||||
// Remove destination file if already exists.
|
||||
releasesDir.remove(finalPackageName);
|
||||
|
||||
// Note: NSIS have to be added to PATH (environment variables).
|
||||
var nsis = childProcess.spawn('makensis', [
|
||||
tmpDir.path('installer.nsi')
|
||||
], {
|
||||
stdio: 'inherit'
|
||||
});
|
||||
nsis.on('error', function (err) {
|
||||
if (err.message === 'spawn makensis ENOENT') {
|
||||
throw 'Can\'t find NSIS. Are you sure you\'ve installed it and added to PATH environment variable?';
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
nsis.on('close', function () {
|
||||
gulpUtil.log('Installer ready!', releasesDir.path(finalPackageName));
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
var cleanClutter = function () {
|
||||
return tmpDir.removeAsync('.');
|
||||
};
|
||||
|
||||
module.exports = function () {
|
||||
return init()
|
||||
.then(copyRuntime)
|
||||
.then(cleanupRuntime)
|
||||
.then(packageBuiltApp)
|
||||
.then(finalize)
|
||||
.then(renameApp)
|
||||
.then(createInstaller)
|
||||
.then(cleanClutter)
|
||||
.catch(console.error);
|
||||
};
|
|
@ -1,64 +1,15 @@
|
|||
'use strict';
|
||||
|
||||
var Q = require('q');
|
||||
var electron = require('electron');
|
||||
var pathUtil = require('path');
|
||||
var childProcess = require('child_process');
|
||||
var kill = require('tree-kill');
|
||||
var utils = require('./utils');
|
||||
var watch;
|
||||
var electron = require('electron');
|
||||
var gulp = require('gulp');
|
||||
|
||||
var gulpPath = pathUtil.resolve('./node_modules/.bin/gulp');
|
||||
|
||||
var runBuild = function () {
|
||||
var deferred = Q.defer();
|
||||
|
||||
var build = childProcess.spawn(utils.spawnablePath(gulpPath), [
|
||||
'build',
|
||||
'--env=' + utils.getEnvName(),
|
||||
'--color'
|
||||
], {
|
||||
gulp.task('start', ['build', 'watch'], function () {
|
||||
childProcess.spawn(electron, ['.'], {
|
||||
stdio: 'inherit'
|
||||
});
|
||||
|
||||
build.on('close', function (code) {
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
var runGulpWatch = function () {
|
||||
watch = childProcess.spawn(utils.spawnablePath(gulpPath), [
|
||||
'watch',
|
||||
'--env=' + utils.getEnvName(),
|
||||
'--color'
|
||||
], {
|
||||
stdio: 'inherit'
|
||||
});
|
||||
|
||||
watch.on('close', function (code) {
|
||||
// Gulp watch exits when error occured during build.
|
||||
// Just respawn it then.
|
||||
runGulpWatch();
|
||||
});
|
||||
};
|
||||
|
||||
var runApp = function () {
|
||||
var app = childProcess.spawn(electron, ['./build'], {
|
||||
stdio: 'inherit'
|
||||
});
|
||||
|
||||
app.on('close', function (code) {
|
||||
})
|
||||
.on('close', function () {
|
||||
// User closed the app. Kill the host process.
|
||||
kill(watch.pid, 'SIGKILL', function () {
|
||||
process.exit();
|
||||
});
|
||||
process.exit();
|
||||
});
|
||||
};
|
||||
|
||||
runBuild()
|
||||
.then(function () {
|
||||
runGulpWatch();
|
||||
runApp();
|
||||
});
|
||||
|
|
|
@ -1,63 +1,11 @@
|
|||
'use strict';
|
||||
|
||||
var argv = require('yargs').argv;
|
||||
var os = require('os');
|
||||
var argv = require('minimist')(process.argv);
|
||||
|
||||
module.exports.os = function () {
|
||||
switch (os.platform()) {
|
||||
case 'darwin':
|
||||
return 'osx';
|
||||
case 'linux':
|
||||
return 'linux';
|
||||
case 'win32':
|
||||
return 'windows';
|
||||
}
|
||||
return 'unsupported';
|
||||
};
|
||||
|
||||
module.exports.replace = function (str, patterns) {
|
||||
Object.keys(patterns).forEach(function (pattern) {
|
||||
var matcher = new RegExp('{{' + pattern + '}}', 'g');
|
||||
str = str.replace(matcher, patterns[pattern]);
|
||||
});
|
||||
return str;
|
||||
};
|
||||
|
||||
module.exports.getReleasePackageName = function(manifest) {
|
||||
return module.exports.replace(manifest.packageNameTemplate, {
|
||||
name: manifest.name,
|
||||
version: process.env.TRAVIS_TAG || process.env.APPVEYOR_REPO_TAG_NAME || manifest.version,
|
||||
build: process.env.TRAVIS_BUILD_NUMBER || process.env.APPVEYOR_BUILD_NUMBER || manifest.build,
|
||||
productName: manifest.productName,
|
||||
platform: process.platform,
|
||||
arch: process.arch
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.getEnvName = function () {
|
||||
exports.getEnvName = function () {
|
||||
return argv.env || 'development';
|
||||
};
|
||||
|
||||
module.exports.getSigningId = function (manifest) {
|
||||
return argv.sign || (manifest.codeSignIdentity ? manifest.codeSignIdentity.dmg : undefined);
|
||||
};
|
||||
|
||||
module.exports.getMASSigningId = function (manifest) {
|
||||
return argv['mas-sign'] || (manifest.codeSignIdentity ? manifest.codeSignIdentity.MAS : undefined);
|
||||
};
|
||||
|
||||
module.exports.getMASInstallerSigningId = function (manifest) {
|
||||
return argv['mas-installer-sign'] || (manifest.codeSignIdentity ? manifest.codeSignIdentity.MASInstaller : undefined);
|
||||
};
|
||||
|
||||
module.exports.releaseForMAS = function () {
|
||||
return !!argv.mas;
|
||||
};
|
||||
|
||||
// Fixes https://github.com/nodejs/node-v0.x-archive/issues/2318
|
||||
module.exports.spawnablePath = function (path) {
|
||||
if (process.platform === 'win32') {
|
||||
return path + '.cmd';
|
||||
}
|
||||
return path;
|
||||
exports.beepSound = function () {
|
||||
process.stdout.write('\u0007');
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue