mirror of
https://github.com/Llewellynvdm/nativefier.git
synced 2024-12-23 02:28:55 +00:00
Update eslint and use Airbnb style
- Add `npm run lint:fix` command - Cleanup inferIcon.js logic slightly
This commit is contained in:
parent
461c7a38f0
commit
8f78dd03af
49
.eslintrc.js
49
.eslintrc.js
@ -1,49 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
globals: {
|
|
||||||
// mocha
|
|
||||||
describe: false,
|
|
||||||
it: false,
|
|
||||||
before: false,
|
|
||||||
beforeEach: false,
|
|
||||||
after: false,
|
|
||||||
afterEach: false
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
indent: [
|
|
||||||
2,
|
|
||||||
4,
|
|
||||||
{SwitchCase: 1}
|
|
||||||
],
|
|
||||||
quotes: [
|
|
||||||
2,
|
|
||||||
'single'
|
|
||||||
],
|
|
||||||
'linebreak-style': [
|
|
||||||
2,
|
|
||||||
'unix'
|
|
||||||
],
|
|
||||||
semi: [
|
|
||||||
2,
|
|
||||||
'always'
|
|
||||||
],
|
|
||||||
'max-len': 0,
|
|
||||||
'require-jsdoc': 0,
|
|
||||||
'padded-blocks': 0,
|
|
||||||
'no-throw-literal': 0,
|
|
||||||
camelcase: 0,
|
|
||||||
'valid-jsdoc': 0,
|
|
||||||
'no-path-concat': 1,
|
|
||||||
'quote-props': [2, 'as-needed'],
|
|
||||||
'no-warning-comments': 1,
|
|
||||||
'no-control-regex': 0
|
|
||||||
},
|
|
||||||
env: {
|
|
||||||
es6: true,
|
|
||||||
browser: true,
|
|
||||||
node: true
|
|
||||||
},
|
|
||||||
ecmaFeatures: {
|
|
||||||
modules: true
|
|
||||||
},
|
|
||||||
extends: 'google'
|
|
||||||
};
|
|
8
.eslintrc.yml
Normal file
8
.eslintrc.yml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
extends: airbnb-base
|
||||||
|
plugins:
|
||||||
|
- import
|
||||||
|
rules:
|
||||||
|
# TODO: Remove this when we have shifted away from the async package
|
||||||
|
no-shadow: 'warn'
|
||||||
|
# Gulpfiles and tests use dev dependencies
|
||||||
|
import/no-extraneous-dependencies: ['error', { devDependencies: ['gulpfile.babel.js', 'gulp/**/**.js', 'test/**/**.js']}]
|
20
.github/CONTRIBUTING.md
vendored
20
.github/CONTRIBUTING.md
vendored
@ -15,23 +15,19 @@ Please include the following in your new issue:
|
|||||||
|
|
||||||
See [here](https://github.com/jiahaog/nativefier#development) for instructions on how to set up a development environment.
|
See [here](https://github.com/jiahaog/nativefier#development) for instructions on how to set up a development environment.
|
||||||
|
|
||||||
Follow the current code style, and make sure tests and lints pass before submitting with the following commands:
|
We follow the [Airbnb Style Guide](https://github.com/airbnb/javascript), please make sure tests and lints pass when you submit your pull request.
|
||||||
|
|
||||||
Run the following command before submitting the pull request:
|
The following commands might be helpful:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Run tests and linting
|
# Run specs and lint
|
||||||
$ npm run ci
|
npm run ci
|
||||||
```
|
|
||||||
|
|
||||||
Or you can run them separately:
|
# Run specs only
|
||||||
|
npm run test
|
||||||
|
|
||||||
```bash
|
# Run linter only
|
||||||
# Tests
|
npm run lint
|
||||||
$ npm run test
|
|
||||||
|
|
||||||
# Lint source files
|
|
||||||
$ npm run lint
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Thank you so much for your contribution!
|
Thank you so much for your contribution!
|
||||||
|
2
app/.eslintrc.yml
Normal file
2
app/.eslintrc.yml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
settings:
|
||||||
|
import/core-modules: [ electron ]
|
@ -1,47 +1,48 @@
|
|||||||
import {Menu, ipcMain, shell, clipboard, BrowserWindow} from 'electron';
|
// Because we are changing the properties of `mainWindow` in initContextMenu()
|
||||||
|
/* eslint-disable no-param-reassign */
|
||||||
|
import { Menu, ipcMain, shell, clipboard, BrowserWindow } from 'electron';
|
||||||
|
|
||||||
function initContextMenu(mainWindow) {
|
function initContextMenu(mainWindow) {
|
||||||
ipcMain.on('contextMenuOpened', (event, targetHref) => {
|
ipcMain.on('contextMenuOpened', (event, targetHref) => {
|
||||||
const contextMenuTemplate = [
|
const contextMenuTemplate = [
|
||||||
{
|
{
|
||||||
label: 'Open with default browser',
|
label: 'Open with default browser',
|
||||||
click: () => {
|
click: () => {
|
||||||
if (targetHref) {
|
if (targetHref) {
|
||||||
shell.openExternal(targetHref);
|
shell.openExternal(targetHref);
|
||||||
return;
|
}
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
{
|
||||||
{
|
label: 'Open in new window',
|
||||||
label: 'Open in new window',
|
click: () => {
|
||||||
click: () => {
|
if (targetHref) {
|
||||||
if (targetHref) {
|
new BrowserWindow().loadURL(targetHref);
|
||||||
new BrowserWindow().loadURL(targetHref);
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
mainWindow.useDefaultWindowBehaviour = true;
|
mainWindow.useDefaultWindowBehaviour = true;
|
||||||
mainWindow.webContents.send('contextMenuClosed');
|
mainWindow.webContents.send('contextMenuClosed');
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Copy link location',
|
label: 'Copy link location',
|
||||||
click: () => {
|
click: () => {
|
||||||
if (targetHref) {
|
if (targetHref) {
|
||||||
clipboard.writeText(targetHref);
|
clipboard.writeText(targetHref);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mainWindow.useDefaultWindowBehaviour = true;
|
mainWindow.useDefaultWindowBehaviour = true;
|
||||||
mainWindow.webContents.send('contextMenuClosed');
|
mainWindow.webContents.send('contextMenuClosed');
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const contextMenu = Menu.buildFromTemplate(contextMenuTemplate);
|
const contextMenu = Menu.buildFromTemplate(contextMenuTemplate);
|
||||||
contextMenu.popup(mainWindow);
|
contextMenu.popup(mainWindow);
|
||||||
mainWindow.contextMenuOpen = true;
|
mainWindow.contextMenuOpen = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export default initContextMenu;
|
export default initContextMenu;
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
import {BrowserWindow, ipcMain} from 'electron';
|
import { BrowserWindow, ipcMain } from 'electron';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
function createLoginWindow(loginCallback) {
|
function createLoginWindow(loginCallback) {
|
||||||
var loginWindow = new BrowserWindow({
|
const loginWindow = new BrowserWindow({
|
||||||
width: 300,
|
width: 300,
|
||||||
height: 400,
|
height: 400,
|
||||||
frame: false,
|
frame: false,
|
||||||
resizable: false
|
resizable: false,
|
||||||
});
|
});
|
||||||
loginWindow.loadURL('file://' + path.join(__dirname, '/static/login/login.html'));
|
loginWindow.loadURL(`file://${path.join(__dirname, '/static/login/login.html')}`);
|
||||||
|
|
||||||
ipcMain.once('login-message', function(event, usernameAndPassword) {
|
ipcMain.once('login-message', (event, usernameAndPassword) => {
|
||||||
loginCallback(usernameAndPassword[0], usernameAndPassword[1]);
|
loginCallback(usernameAndPassword[0], usernameAndPassword[1]);
|
||||||
loginWindow.close();
|
loginWindow.close();
|
||||||
});
|
});
|
||||||
return loginWindow;
|
return loginWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default createLoginWindow;
|
export default createLoginWindow;
|
||||||
|
@ -1,232 +1,234 @@
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import {BrowserWindow, shell, ipcMain, dialog} from 'electron';
|
import { BrowserWindow, shell, ipcMain, dialog } from 'electron';
|
||||||
import windowStateKeeper from 'electron-window-state';
|
import windowStateKeeper from 'electron-window-state';
|
||||||
import helpers from './../../helpers/helpers';
|
import helpers from './../../helpers/helpers';
|
||||||
import createMenu from './../menu/menu';
|
import createMenu from './../menu/menu';
|
||||||
import initContextMenu from './../contextMenu/contextMenu';
|
import initContextMenu from './../contextMenu/contextMenu';
|
||||||
|
|
||||||
const {isOSX, linkIsInternal, getCssToInject, shouldInjectCss} = helpers;
|
const { isOSX, linkIsInternal, getCssToInject, shouldInjectCss } = helpers;
|
||||||
|
|
||||||
const ZOOM_INTERVAL = 0.1;
|
const ZOOM_INTERVAL = 0.1;
|
||||||
|
|
||||||
|
function maybeHideWindow(window, event, fastQuit) {
|
||||||
|
if (isOSX() && !fastQuit) {
|
||||||
|
// this is called when exiting from clicking the cross button on the window
|
||||||
|
event.preventDefault();
|
||||||
|
window.hide();
|
||||||
|
}
|
||||||
|
// will close the window on other platforms
|
||||||
|
}
|
||||||
|
|
||||||
|
function maybeInjectCss(browserWindow) {
|
||||||
|
if (!shouldInjectCss()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cssToInject = getCssToInject();
|
||||||
|
|
||||||
|
const injectCss = () => {
|
||||||
|
browserWindow.webContents.insertCSS(cssToInject);
|
||||||
|
};
|
||||||
|
|
||||||
|
browserWindow.webContents.on('did-finish-load', () => {
|
||||||
|
// remove the injection of css the moment the page is loaded
|
||||||
|
browserWindow.webContents.removeListener('did-get-response-details', injectCss);
|
||||||
|
});
|
||||||
|
|
||||||
|
// on every page navigation inject the css
|
||||||
|
browserWindow.webContents.on('did-navigate', () => {
|
||||||
|
// we have to inject the css in did-get-response-details to prevent the fouc
|
||||||
|
// will run multiple times
|
||||||
|
browserWindow.webContents.on('did-get-response-details', injectCss);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {{}} options AppArgs from nativefier.json
|
* @param {{}} inpOptions AppArgs from nativefier.json
|
||||||
* @param {function} onAppQuit
|
* @param {function} onAppQuit
|
||||||
* @param {function} setDockBadge
|
* @param {function} setDockBadge
|
||||||
* @returns {electron.BrowserWindow}
|
* @returns {electron.BrowserWindow}
|
||||||
*/
|
*/
|
||||||
function createMainWindow(options, onAppQuit, setDockBadge) {
|
function createMainWindow(inpOptions, onAppQuit, setDockBadge) {
|
||||||
const mainWindowState = windowStateKeeper({
|
const options = Object.assign({}, inpOptions);
|
||||||
defaultWidth: options.width || 1280,
|
const mainWindowState = windowStateKeeper({
|
||||||
defaultHeight: options.height || 800
|
defaultWidth: options.width || 1280,
|
||||||
});
|
defaultHeight: options.height || 800,
|
||||||
|
});
|
||||||
|
|
||||||
const mainWindow = new BrowserWindow({
|
const mainWindow = new BrowserWindow({
|
||||||
frame: !options.hideWindowFrame,
|
frame: !options.hideWindowFrame,
|
||||||
width: mainWindowState.width,
|
width: mainWindowState.width,
|
||||||
height: mainWindowState.height,
|
height: mainWindowState.height,
|
||||||
minWidth: options.minWidth,
|
minWidth: options.minWidth,
|
||||||
minHeight: options.minHeight,
|
minHeight: options.minHeight,
|
||||||
maxWidth: options.maxWidth,
|
maxWidth: options.maxWidth,
|
||||||
maxHeight: options.maxHeight,
|
maxHeight: options.maxHeight,
|
||||||
x: mainWindowState.x,
|
x: mainWindowState.x,
|
||||||
y: mainWindowState.y,
|
y: mainWindowState.y,
|
||||||
autoHideMenuBar: !options.showMenuBar,
|
autoHideMenuBar: !options.showMenuBar,
|
||||||
// Convert dashes to spaces because on linux the app name is joined with dashes
|
// Convert dashes to spaces because on linux the app name is joined with dashes
|
||||||
title: options.name,
|
title: options.name,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
javascript: true,
|
javascript: true,
|
||||||
plugins: true,
|
plugins: true,
|
||||||
// node globals causes problems with sites like messenger.com
|
// node globals causes problems with sites like messenger.com
|
||||||
nodeIntegration: false,
|
nodeIntegration: false,
|
||||||
webSecurity: !options.insecure,
|
webSecurity: !options.insecure,
|
||||||
preload: path.join(__dirname, 'static', 'preload.js'),
|
preload: path.join(__dirname, 'static', 'preload.js'),
|
||||||
zoomFactor: options.zoom
|
zoomFactor: options.zoom,
|
||||||
},
|
},
|
||||||
// after webpack path here should reference `resources/app/`
|
// after webpack path here should reference `resources/app/`
|
||||||
icon: path.join(__dirname, '../', '/icon.png'),
|
icon: path.join(__dirname, '../', '/icon.png'),
|
||||||
// set to undefined and not false because explicitly setting to false will disable full screen
|
// set to undefined and not false because explicitly setting to false will disable full screen
|
||||||
fullscreen: options.fullScreen || undefined
|
fullscreen: options.fullScreen || undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
mainWindowState.manage(mainWindow);
|
mainWindowState.manage(mainWindow);
|
||||||
|
|
||||||
// after first run, no longer force full screen to be true
|
// after first run, no longer force full screen to be true
|
||||||
if (options.fullScreen) {
|
if (options.fullScreen) {
|
||||||
options.fullScreen = undefined;
|
options.fullScreen = undefined;
|
||||||
fs.writeFileSync(path.join(__dirname, '..', 'nativefier.json'), JSON.stringify(options));
|
fs.writeFileSync(path.join(__dirname, '..', 'nativefier.json'), JSON.stringify(options));
|
||||||
}
|
}
|
||||||
|
|
||||||
// after first run, no longer force maximize to be true
|
// after first run, no longer force maximize to be true
|
||||||
if (options.maximize) {
|
if (options.maximize) {
|
||||||
mainWindow.maximize();
|
mainWindow.maximize();
|
||||||
options.maximize = undefined;
|
options.maximize = undefined;
|
||||||
fs.writeFileSync(path.join(__dirname, '..', 'nativefier.json'), JSON.stringify(options));
|
fs.writeFileSync(path.join(__dirname, '..', 'nativefier.json'), JSON.stringify(options));
|
||||||
}
|
}
|
||||||
|
|
||||||
let currentZoom = options.zoom;
|
let currentZoom = options.zoom;
|
||||||
|
|
||||||
const onZoomIn = () => {
|
const onZoomIn = () => {
|
||||||
currentZoom += ZOOM_INTERVAL;
|
currentZoom += ZOOM_INTERVAL;
|
||||||
mainWindow.webContents.send('change-zoom', currentZoom);
|
mainWindow.webContents.send('change-zoom', currentZoom);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onZoomOut = () => {
|
const onZoomOut = () => {
|
||||||
currentZoom -= ZOOM_INTERVAL;
|
currentZoom -= ZOOM_INTERVAL;
|
||||||
mainWindow.webContents.send('change-zoom', currentZoom);
|
mainWindow.webContents.send('change-zoom', currentZoom);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onZoomReset = () => {
|
const onZoomReset = () => {
|
||||||
mainWindow.webContents.send('change-zoom', options.zoom);
|
mainWindow.webContents.send('change-zoom', options.zoom);
|
||||||
};
|
};
|
||||||
|
|
||||||
const clearAppData = () => {
|
const clearAppData = () => {
|
||||||
dialog.showMessageBox(mainWindow, {
|
dialog.showMessageBox(mainWindow, {
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
buttons: ['Yes', 'Cancel'],
|
buttons: ['Yes', 'Cancel'],
|
||||||
defaultId: 1,
|
defaultId: 1,
|
||||||
title: 'Clear cache confirmation',
|
title: 'Clear cache confirmation',
|
||||||
message: 'This will clear all data (cookies, local storage etc) from this app. Are you sure you wish to proceed?'
|
message: 'This will clear all data (cookies, local storage etc) from this app. Are you sure you wish to proceed?',
|
||||||
}, response => {
|
}, (response) => {
|
||||||
if (response === 0) {
|
if (response !== 0) {
|
||||||
const session = mainWindow.webContents.session;
|
return;
|
||||||
session.clearStorageData(() => {
|
}
|
||||||
session.clearCache(() => {
|
const session = mainWindow.webContents.session;
|
||||||
mainWindow.loadURL(options.targetUrl);
|
session.clearStorageData(() => {
|
||||||
});
|
session.clearCache(() => {
|
||||||
});
|
mainWindow.loadURL(options.targetUrl);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
};
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const onGoBack = () => {
|
const onGoBack = () => {
|
||||||
mainWindow.webContents.goBack();
|
mainWindow.webContents.goBack();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onGoForward = () => {
|
const onGoForward = () => {
|
||||||
mainWindow.webContents.goForward();
|
mainWindow.webContents.goForward();
|
||||||
};
|
};
|
||||||
|
|
||||||
const getCurrentUrl = () => {
|
const getCurrentUrl = () => mainWindow.webContents.getURL();
|
||||||
return mainWindow.webContents.getURL();
|
|
||||||
};
|
|
||||||
|
|
||||||
const menuOptions = {
|
const menuOptions = {
|
||||||
nativefierVersion: options.nativefierVersion,
|
nativefierVersion: options.nativefierVersion,
|
||||||
appQuit: onAppQuit,
|
appQuit: onAppQuit,
|
||||||
zoomIn: onZoomIn,
|
zoomIn: onZoomIn,
|
||||||
zoomOut: onZoomOut,
|
zoomOut: onZoomOut,
|
||||||
zoomReset: onZoomReset,
|
zoomReset: onZoomReset,
|
||||||
zoomBuildTimeValue: options.zoom,
|
zoomBuildTimeValue: options.zoom,
|
||||||
goBack: onGoBack,
|
goBack: onGoBack,
|
||||||
goForward: onGoForward,
|
goForward: onGoForward,
|
||||||
getCurrentUrl: getCurrentUrl,
|
getCurrentUrl,
|
||||||
clearAppData: clearAppData,
|
clearAppData,
|
||||||
disableDevTools: options.disableDevTools
|
disableDevTools: options.disableDevTools,
|
||||||
};
|
};
|
||||||
|
|
||||||
createMenu(menuOptions);
|
createMenu(menuOptions);
|
||||||
if (!options.disableContextMenu) {
|
if (!options.disableContextMenu) {
|
||||||
initContextMenu(mainWindow);
|
initContextMenu(mainWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.userAgent) {
|
||||||
|
mainWindow.webContents.setUserAgent(options.userAgent);
|
||||||
|
}
|
||||||
|
|
||||||
|
maybeInjectCss(mainWindow);
|
||||||
|
mainWindow.webContents.on('did-finish-load', () => {
|
||||||
|
mainWindow.webContents.send('params', JSON.stringify(options));
|
||||||
|
});
|
||||||
|
|
||||||
|
if (options.counter) {
|
||||||
|
mainWindow.on('page-title-updated', (e, title) => {
|
||||||
|
const itemCountRegex = /[([{](\d*?)[}\])]/;
|
||||||
|
const match = itemCountRegex.exec(title);
|
||||||
|
if (match) {
|
||||||
|
setDockBadge(match[1]);
|
||||||
|
} else {
|
||||||
|
setDockBadge('');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
ipcMain.on('notification', () => {
|
||||||
|
if (!isOSX() || mainWindow.isFocused()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setDockBadge('•');
|
||||||
|
});
|
||||||
|
mainWindow.on('focus', () => {
|
||||||
|
setDockBadge('');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
mainWindow.webContents.on('new-window', (event, urlToGo) => {
|
||||||
|
if (mainWindow.useDefaultWindowBehaviour) {
|
||||||
|
mainWindow.useDefaultWindowBehaviour = false;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.userAgent) {
|
if (linkIsInternal(options.targetUrl, urlToGo, options.internalUrls)) {
|
||||||
mainWindow.webContents.setUserAgent(options.userAgent);
|
return;
|
||||||
}
|
}
|
||||||
|
event.preventDefault();
|
||||||
|
shell.openExternal(urlToGo);
|
||||||
|
});
|
||||||
|
|
||||||
maybeInjectCss(mainWindow);
|
mainWindow.loadURL(options.targetUrl);
|
||||||
mainWindow.webContents.on('did-finish-load', () => {
|
|
||||||
mainWindow.webContents.send('params', JSON.stringify(options));
|
|
||||||
});
|
|
||||||
|
|
||||||
if (options.counter) {
|
mainWindow.on('close', (event) => {
|
||||||
mainWindow.on('page-title-updated', (e, title) => {
|
if (mainWindow.isFullScreen()) {
|
||||||
const itemCountRegex = /[\(\[{](\d*?)[}\]\)]/;
|
mainWindow.setFullScreen(false);
|
||||||
const match = itemCountRegex.exec(title);
|
mainWindow.once('leave-full-screen', maybeHideWindow.bind(this, mainWindow, event, options.fastQuit));
|
||||||
if (match) {
|
|
||||||
setDockBadge(match[1]);
|
|
||||||
} else {
|
|
||||||
setDockBadge('');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
ipcMain.on('notification', () => {
|
|
||||||
if (!isOSX() || mainWindow.isFocused()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setDockBadge('•');
|
|
||||||
});
|
|
||||||
mainWindow.on('focus', () => {
|
|
||||||
setDockBadge('');
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
maybeHideWindow(mainWindow, event, options.fastQuit);
|
||||||
|
});
|
||||||
|
|
||||||
mainWindow.webContents.on('new-window', (event, urlToGo) => {
|
return mainWindow;
|
||||||
if (mainWindow.useDefaultWindowBehaviour) {
|
|
||||||
mainWindow.useDefaultWindowBehaviour = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (linkIsInternal(options.targetUrl, urlToGo, options.internalUrls)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
event.preventDefault();
|
|
||||||
shell.openExternal(urlToGo);
|
|
||||||
});
|
|
||||||
|
|
||||||
mainWindow.loadURL(options.targetUrl);
|
|
||||||
|
|
||||||
mainWindow.on('close', event => {
|
|
||||||
if (mainWindow.isFullScreen()) {
|
|
||||||
mainWindow.setFullScreen(false);
|
|
||||||
mainWindow.once('leave-full-screen', maybeHideWindow.bind(this, mainWindow, event, options.fastQuit));
|
|
||||||
}
|
|
||||||
maybeHideWindow(mainWindow, event, options.fastQuit);
|
|
||||||
});
|
|
||||||
|
|
||||||
return mainWindow;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ipcMain.on('cancelNewWindowOverride', () => {
|
ipcMain.on('cancelNewWindowOverride', () => {
|
||||||
const allWindows = BrowserWindow.getAllWindows();
|
const allWindows = BrowserWindow.getAllWindows();
|
||||||
allWindows.forEach(window => {
|
allWindows.forEach((window) => {
|
||||||
window.useDefaultWindowBehaviour = false;
|
// eslint-disable-next-line no-param-reassign
|
||||||
});
|
window.useDefaultWindowBehaviour = false;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function maybeHideWindow(window, event, fastQuit) {
|
|
||||||
if (isOSX() && !fastQuit) {
|
|
||||||
// this is called when exiting from clicking the cross button on the window
|
|
||||||
event.preventDefault();
|
|
||||||
window.hide();
|
|
||||||
}
|
|
||||||
// will close the window on other platforms
|
|
||||||
}
|
|
||||||
|
|
||||||
function maybeInjectCss(browserWindow) {
|
|
||||||
if (!shouldInjectCss()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const cssToInject = getCssToInject();
|
|
||||||
|
|
||||||
const injectCss = () => {
|
|
||||||
browserWindow.webContents.insertCSS(cssToInject);
|
|
||||||
};
|
|
||||||
|
|
||||||
browserWindow.webContents.on('did-finish-load', () => {
|
|
||||||
// remove the injection of css the moment the page is loaded
|
|
||||||
browserWindow.webContents.removeListener('did-get-response-details', injectCss);
|
|
||||||
});
|
|
||||||
|
|
||||||
// on every page navigation inject the css
|
|
||||||
browserWindow.webContents.on('did-navigate', () => {
|
|
||||||
// we have to inject the css in did-get-response-details to prevent the fouc
|
|
||||||
// will run multiple times
|
|
||||||
browserWindow.webContents.on('did-get-response-details', injectCss);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export default createMainWindow;
|
export default createMainWindow;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {Menu, shell, clipboard} from 'electron';
|
import { Menu, shell, clipboard } from 'electron';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param nativefierVersion
|
* @param nativefierVersion
|
||||||
@ -13,255 +13,265 @@ import {Menu, shell, clipboard} from 'electron';
|
|||||||
* @param clearAppData
|
* @param clearAppData
|
||||||
* @param disableDevTools
|
* @param disableDevTools
|
||||||
*/
|
*/
|
||||||
function createMenu({nativefierVersion, appQuit, zoomIn, zoomOut, zoomReset, zoomBuildTimeValue, goBack, goForward, getCurrentUrl, clearAppData, disableDevTools}) {
|
function createMenu({ nativefierVersion,
|
||||||
if (Menu.getApplicationMenu()) {
|
appQuit,
|
||||||
return;
|
zoomIn,
|
||||||
}
|
zoomOut,
|
||||||
const zoomResetLabel = (zoomBuildTimeValue === 1.0) ?
|
zoomReset,
|
||||||
|
zoomBuildTimeValue,
|
||||||
|
goBack,
|
||||||
|
goForward,
|
||||||
|
getCurrentUrl,
|
||||||
|
clearAppData,
|
||||||
|
disableDevTools }) {
|
||||||
|
if (Menu.getApplicationMenu()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const zoomResetLabel = (zoomBuildTimeValue === 1.0) ?
|
||||||
'Reset Zoom' :
|
'Reset Zoom' :
|
||||||
`Reset Zoom (to ${zoomBuildTimeValue * 100}%, set at build time)`;
|
`Reset Zoom (to ${zoomBuildTimeValue * 100}%, set at build time)`;
|
||||||
|
|
||||||
const template = [
|
const template = [
|
||||||
|
{
|
||||||
|
label: 'Edit',
|
||||||
|
submenu: [
|
||||||
{
|
{
|
||||||
label: 'Edit',
|
label: 'Undo',
|
||||||
submenu: [
|
accelerator: 'CmdOrCtrl+Z',
|
||||||
{
|
role: 'undo',
|
||||||
label: 'Undo',
|
|
||||||
accelerator: 'CmdOrCtrl+Z',
|
|
||||||
role: 'undo'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Redo',
|
|
||||||
accelerator: 'Shift+CmdOrCtrl+Z',
|
|
||||||
role: 'redo'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'separator'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Cut',
|
|
||||||
accelerator: 'CmdOrCtrl+X',
|
|
||||||
role: 'cut'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Copy',
|
|
||||||
accelerator: 'CmdOrCtrl+C',
|
|
||||||
role: 'copy'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Copy Current URL',
|
|
||||||
accelerator: 'CmdOrCtrl+L',
|
|
||||||
click: () => {
|
|
||||||
const currentURL = getCurrentUrl();
|
|
||||||
clipboard.writeText(currentURL);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Paste',
|
|
||||||
accelerator: 'CmdOrCtrl+V',
|
|
||||||
role: 'paste'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Select All',
|
|
||||||
accelerator: 'CmdOrCtrl+A',
|
|
||||||
role: 'selectall'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Clear App Data',
|
|
||||||
click: () => {
|
|
||||||
clearAppData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'View',
|
label: 'Redo',
|
||||||
submenu: [
|
accelerator: 'Shift+CmdOrCtrl+Z',
|
||||||
{
|
role: 'redo',
|
||||||
label: 'Back',
|
|
||||||
accelerator: 'CmdOrCtrl+[',
|
|
||||||
click: () => {
|
|
||||||
goBack();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Forward',
|
|
||||||
accelerator: 'CmdOrCtrl+]',
|
|
||||||
click: () => {
|
|
||||||
goForward();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Reload',
|
|
||||||
accelerator: 'CmdOrCtrl+R',
|
|
||||||
click: (item, focusedWindow) => {
|
|
||||||
if (focusedWindow) {
|
|
||||||
focusedWindow.reload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'separator'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Toggle Full Screen',
|
|
||||||
accelerator: (() => {
|
|
||||||
if (process.platform === 'darwin') {
|
|
||||||
return 'Ctrl+Command+F';
|
|
||||||
}
|
|
||||||
return 'F11';
|
|
||||||
})(),
|
|
||||||
click: (item, focusedWindow) => {
|
|
||||||
if (focusedWindow) {
|
|
||||||
focusedWindow.setFullScreen(!focusedWindow.isFullScreen());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Zoom In',
|
|
||||||
accelerator: (() => {
|
|
||||||
if (process.platform === 'darwin') {
|
|
||||||
return 'Command+=';
|
|
||||||
}
|
|
||||||
return 'Ctrl+=';
|
|
||||||
})(),
|
|
||||||
click: () => {
|
|
||||||
zoomIn();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Zoom Out',
|
|
||||||
accelerator: (() => {
|
|
||||||
if (process.platform === 'darwin') {
|
|
||||||
return 'Command+-';
|
|
||||||
}
|
|
||||||
return 'Ctrl+-';
|
|
||||||
})(),
|
|
||||||
click: () => {
|
|
||||||
zoomOut();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: zoomResetLabel,
|
|
||||||
accelerator: (() => {
|
|
||||||
if (process.platform === 'darwin') {
|
|
||||||
return 'Command+0';
|
|
||||||
}
|
|
||||||
return 'Ctrl+0';
|
|
||||||
})(),
|
|
||||||
click: () => {
|
|
||||||
zoomReset();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Toggle Developer Tools',
|
|
||||||
accelerator: (() => {
|
|
||||||
if (process.platform === 'darwin') {
|
|
||||||
return 'Alt+Command+I';
|
|
||||||
}
|
|
||||||
return 'Ctrl+Shift+I';
|
|
||||||
})(),
|
|
||||||
click: (item, focusedWindow) => {
|
|
||||||
if (focusedWindow) {
|
|
||||||
focusedWindow.toggleDevTools();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Window',
|
type: 'separator',
|
||||||
role: 'window',
|
|
||||||
submenu: [
|
|
||||||
{
|
|
||||||
label: 'Minimize',
|
|
||||||
accelerator: 'CmdOrCtrl+M',
|
|
||||||
role: 'minimize'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Close',
|
|
||||||
accelerator: 'CmdOrCtrl+W',
|
|
||||||
role: 'close'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Help',
|
label: 'Cut',
|
||||||
role: 'help',
|
accelerator: 'CmdOrCtrl+X',
|
||||||
submenu: [
|
role: 'cut',
|
||||||
{
|
},
|
||||||
label: `Built with Nativefier v${nativefierVersion}`,
|
{
|
||||||
click: () => {
|
label: 'Copy',
|
||||||
shell.openExternal('https://github.com/jiahaog/nativefier');
|
accelerator: 'CmdOrCtrl+C',
|
||||||
}
|
role: 'copy',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Report an Issue',
|
label: 'Copy Current URL',
|
||||||
click: () => {
|
accelerator: 'CmdOrCtrl+L',
|
||||||
shell.openExternal('https://github.com/jiahaog/nativefier/issues');
|
click: () => {
|
||||||
}
|
const currentURL = getCurrentUrl();
|
||||||
}
|
clipboard.writeText(currentURL);
|
||||||
]
|
},
|
||||||
}
|
},
|
||||||
];
|
{
|
||||||
|
label: 'Paste',
|
||||||
if (disableDevTools) {
|
accelerator: 'CmdOrCtrl+V',
|
||||||
// remove last item (dev tools) from menu > view
|
role: 'paste',
|
||||||
const submenu = template[1].submenu;
|
},
|
||||||
submenu.splice(submenu.length - 1, 1);
|
{
|
||||||
}
|
label: 'Select All',
|
||||||
|
accelerator: 'CmdOrCtrl+A',
|
||||||
if (process.platform === 'darwin') {
|
role: 'selectall',
|
||||||
template.unshift({
|
},
|
||||||
label: 'Electron',
|
{
|
||||||
submenu: [
|
label: 'Clear App Data',
|
||||||
{
|
click: () => {
|
||||||
label: 'Services',
|
clearAppData();
|
||||||
role: 'services',
|
},
|
||||||
submenu: []
|
},
|
||||||
},
|
],
|
||||||
{
|
},
|
||||||
type: 'separator'
|
{
|
||||||
},
|
label: 'View',
|
||||||
{
|
submenu: [
|
||||||
label: 'Hide App',
|
{
|
||||||
accelerator: 'Command+H',
|
label: 'Back',
|
||||||
role: 'hide'
|
accelerator: 'CmdOrCtrl+[',
|
||||||
},
|
click: () => {
|
||||||
{
|
goBack();
|
||||||
label: 'Hide Others',
|
},
|
||||||
accelerator: 'Command+Shift+H',
|
},
|
||||||
role: 'hideothers'
|
{
|
||||||
},
|
label: 'Forward',
|
||||||
{
|
accelerator: 'CmdOrCtrl+]',
|
||||||
label: 'Show All',
|
click: () => {
|
||||||
role: 'unhide'
|
goForward();
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
type: 'separator'
|
{
|
||||||
},
|
label: 'Reload',
|
||||||
{
|
accelerator: 'CmdOrCtrl+R',
|
||||||
label: 'Quit',
|
click: (item, focusedWindow) => {
|
||||||
accelerator: 'Command+Q',
|
if (focusedWindow) {
|
||||||
click: () => {
|
focusedWindow.reload();
|
||||||
appQuit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
template[3].submenu.push(
|
|
||||||
{
|
|
||||||
type: 'separator'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Bring All to Front',
|
|
||||||
role: 'front'
|
|
||||||
}
|
}
|
||||||
);
|
},
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
type: 'separator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Toggle Full Screen',
|
||||||
|
accelerator: (() => {
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
return 'Ctrl+Command+F';
|
||||||
|
}
|
||||||
|
return 'F11';
|
||||||
|
})(),
|
||||||
|
click: (item, focusedWindow) => {
|
||||||
|
if (focusedWindow) {
|
||||||
|
focusedWindow.setFullScreen(!focusedWindow.isFullScreen());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Zoom In',
|
||||||
|
accelerator: (() => {
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
return 'Command+=';
|
||||||
|
}
|
||||||
|
return 'Ctrl+=';
|
||||||
|
})(),
|
||||||
|
click: () => {
|
||||||
|
zoomIn();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Zoom Out',
|
||||||
|
accelerator: (() => {
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
return 'Command+-';
|
||||||
|
}
|
||||||
|
return 'Ctrl+-';
|
||||||
|
})(),
|
||||||
|
click: () => {
|
||||||
|
zoomOut();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: zoomResetLabel,
|
||||||
|
accelerator: (() => {
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
return 'Command+0';
|
||||||
|
}
|
||||||
|
return 'Ctrl+0';
|
||||||
|
})(),
|
||||||
|
click: () => {
|
||||||
|
zoomReset();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Toggle Developer Tools',
|
||||||
|
accelerator: (() => {
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
return 'Alt+Command+I';
|
||||||
|
}
|
||||||
|
return 'Ctrl+Shift+I';
|
||||||
|
})(),
|
||||||
|
click: (item, focusedWindow) => {
|
||||||
|
if (focusedWindow) {
|
||||||
|
focusedWindow.toggleDevTools();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Window',
|
||||||
|
role: 'window',
|
||||||
|
submenu: [
|
||||||
|
{
|
||||||
|
label: 'Minimize',
|
||||||
|
accelerator: 'CmdOrCtrl+M',
|
||||||
|
role: 'minimize',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Close',
|
||||||
|
accelerator: 'CmdOrCtrl+W',
|
||||||
|
role: 'close',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Help',
|
||||||
|
role: 'help',
|
||||||
|
submenu: [
|
||||||
|
{
|
||||||
|
label: `Built with Nativefier v${nativefierVersion}`,
|
||||||
|
click: () => {
|
||||||
|
shell.openExternal('https://github.com/jiahaog/nativefier');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Report an Issue',
|
||||||
|
click: () => {
|
||||||
|
shell.openExternal('https://github.com/jiahaog/nativefier/issues');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const menu = Menu.buildFromTemplate(template);
|
if (disableDevTools) {
|
||||||
Menu.setApplicationMenu(menu);
|
// remove last item (dev tools) from menu > view
|
||||||
|
const submenu = template[1].submenu;
|
||||||
|
submenu.splice(submenu.length - 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
template.unshift({
|
||||||
|
label: 'Electron',
|
||||||
|
submenu: [
|
||||||
|
{
|
||||||
|
label: 'Services',
|
||||||
|
role: 'services',
|
||||||
|
submenu: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'separator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Hide App',
|
||||||
|
accelerator: 'Command+H',
|
||||||
|
role: 'hide',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Hide Others',
|
||||||
|
accelerator: 'Command+Shift+H',
|
||||||
|
role: 'hideothers',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Show All',
|
||||||
|
role: 'unhide',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'separator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Quit',
|
||||||
|
accelerator: 'Command+Q',
|
||||||
|
click: () => {
|
||||||
|
appQuit();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
template[3].submenu.push(
|
||||||
|
{
|
||||||
|
type: 'separator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Bring All to Front',
|
||||||
|
role: 'front',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const menu = Menu.buildFromTemplate(template);
|
||||||
|
Menu.setApplicationMenu(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default createMenu;
|
export default createMenu;
|
||||||
|
@ -6,39 +6,39 @@ import path from 'path';
|
|||||||
const INJECT_CSS_PATH = path.join(__dirname, '..', 'inject/inject.css');
|
const INJECT_CSS_PATH = path.join(__dirname, '..', 'inject/inject.css');
|
||||||
|
|
||||||
function isOSX() {
|
function isOSX() {
|
||||||
return os.platform() === 'darwin';
|
return os.platform() === 'darwin';
|
||||||
}
|
}
|
||||||
|
|
||||||
function isLinux() {
|
function isLinux() {
|
||||||
return os.platform() === 'linux';
|
return os.platform() === 'linux';
|
||||||
}
|
}
|
||||||
|
|
||||||
function isWindows() {
|
function isWindows() {
|
||||||
return os.platform() === 'win32';
|
return os.platform() === 'win32';
|
||||||
}
|
}
|
||||||
|
|
||||||
function linkIsInternal(currentUrl, newUrl, internalUrlRegex) {
|
function linkIsInternal(currentUrl, newUrl, internalUrlRegex) {
|
||||||
if (internalUrlRegex) {
|
if (internalUrlRegex) {
|
||||||
var regex = RegExp(internalUrlRegex);
|
const regex = RegExp(internalUrlRegex);
|
||||||
return regex.test(newUrl);
|
return regex.test(newUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentDomain = wurl('domain', currentUrl);
|
const currentDomain = wurl('domain', currentUrl);
|
||||||
var newDomain = wurl('domain', newUrl);
|
const newDomain = wurl('domain', newUrl);
|
||||||
return currentDomain === newDomain;
|
return currentDomain === newDomain;
|
||||||
}
|
}
|
||||||
|
|
||||||
function shouldInjectCss() {
|
function shouldInjectCss() {
|
||||||
try {
|
try {
|
||||||
fs.accessSync(INJECT_CSS_PATH, fs.F_OK);
|
fs.accessSync(INJECT_CSS_PATH, fs.F_OK);
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCssToInject() {
|
function getCssToInject() {
|
||||||
return fs.readFileSync(INJECT_CSS_PATH).toString();
|
return fs.readFileSync(INJECT_CSS_PATH).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,19 +47,20 @@ function getCssToInject() {
|
|||||||
* @param message
|
* @param message
|
||||||
*/
|
*/
|
||||||
function debugLog(browserWindow, message) {
|
function debugLog(browserWindow, message) {
|
||||||
// need the timeout as it takes time for the preload javascript to be loaded in the window
|
// need the timeout as it takes time for the preload javascript to be loaded in the window
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
browserWindow.webContents.send('debug', message);
|
browserWindow.webContents.send('debug', message);
|
||||||
}, 3000);
|
}, 3000);
|
||||||
console.log(message);
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
isOSX,
|
isOSX,
|
||||||
isLinux,
|
isLinux,
|
||||||
isWindows,
|
isWindows,
|
||||||
linkIsInternal,
|
linkIsInternal,
|
||||||
getCssToInject,
|
getCssToInject,
|
||||||
debugLog,
|
debugLog,
|
||||||
shouldInjectCss
|
shouldInjectCss,
|
||||||
};
|
};
|
||||||
|
@ -2,23 +2,7 @@ import fs from 'fs';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import helpers from './helpers';
|
import helpers from './helpers';
|
||||||
|
|
||||||
const {isOSX, isWindows, isLinux} = helpers;
|
const { isOSX, isWindows, isLinux } = helpers;
|
||||||
|
|
||||||
function inferFlash() {
|
|
||||||
if (isOSX()) {
|
|
||||||
return darwinMatch();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isWindows()) {
|
|
||||||
return windowsMatch();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isLinux()) {
|
|
||||||
return linuxMatch();
|
|
||||||
}
|
|
||||||
|
|
||||||
console.warn('Unable to determine OS to infer flash player');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Synchronously find a file or directory
|
* Synchronously find a file or directory
|
||||||
@ -27,56 +11,72 @@ function inferFlash() {
|
|||||||
* @param {boolean} [findDir] if true, search results will be limited to only directories
|
* @param {boolean} [findDir] if true, search results will be limited to only directories
|
||||||
* @returns {Array}
|
* @returns {Array}
|
||||||
*/
|
*/
|
||||||
function findSync(pattern, base, findDir) {
|
function findSync(pattern, basePath, findDir) {
|
||||||
const matches = [];
|
const matches = [];
|
||||||
|
|
||||||
(function findSyncRecurse(base) {
|
(function findSyncRecurse(base) {
|
||||||
let children;
|
let children;
|
||||||
try {
|
try {
|
||||||
children = fs.readdirSync(base);
|
children = fs.readdirSync(base);
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
if (exception.code === 'ENOENT') {
|
if (exception.code === 'ENOENT') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw exception;
|
throw exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
children.forEach((child) => {
|
||||||
|
const childPath = path.join(base, child);
|
||||||
|
const childIsDirectory = fs.lstatSync(childPath).isDirectory();
|
||||||
|
const patternMatches = pattern.test(childPath);
|
||||||
|
|
||||||
|
if (!patternMatches) {
|
||||||
|
if (!childIsDirectory) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
findSyncRecurse(childPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
children.forEach(child => {
|
if (!findDir) {
|
||||||
const childPath = path.join(base, child);
|
matches.push(childPath);
|
||||||
const childIsDirectory = fs.lstatSync(childPath).isDirectory();
|
return;
|
||||||
const patternMatches = pattern.test(childPath);
|
}
|
||||||
|
|
||||||
if (!patternMatches) {
|
if (childIsDirectory) {
|
||||||
if (!childIsDirectory) {
|
matches.push(childPath);
|
||||||
return;
|
}
|
||||||
}
|
});
|
||||||
findSyncRecurse(childPath);
|
}(basePath));
|
||||||
return;
|
return matches;
|
||||||
}
|
|
||||||
|
|
||||||
if (!findDir) {
|
|
||||||
matches.push(childPath);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (childIsDirectory) {
|
|
||||||
matches.push(childPath);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})(base);
|
|
||||||
return matches;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function linuxMatch() {
|
function linuxMatch() {
|
||||||
return findSync(/libpepflashplayer\.so/, '/opt/google/chrome')[0];
|
return findSync(/libpepflashplayer\.so/, '/opt/google/chrome')[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
function windowsMatch() {
|
function windowsMatch() {
|
||||||
return findSync(/pepflashplayer\.dll/, 'C:\\Program Files (x86)\\Google\\Chrome')[0];
|
return findSync(/pepflashplayer\.dll/, 'C:\\Program Files (x86)\\Google\\Chrome')[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
function darwinMatch() {
|
function darwinMatch() {
|
||||||
return findSync(/PepperFlashPlayer.plugin/, '/Applications/Google Chrome.app/', true)[0];
|
return findSync(/PepperFlashPlayer.plugin/, '/Applications/Google Chrome.app/', true)[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function inferFlash() {
|
||||||
|
if (isOSX()) {
|
||||||
|
return darwinMatch();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isWindows()) {
|
||||||
|
return windowsMatch();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLinux()) {
|
||||||
|
return linuxMatch();
|
||||||
|
}
|
||||||
|
|
||||||
|
console.warn('Unable to determine OS to infer flash player');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
export default inferFlash;
|
export default inferFlash;
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
import 'source-map-support/register';
|
import 'source-map-support/register';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import {app, crashReporter} from 'electron';
|
import { app, crashReporter } from 'electron';
|
||||||
|
import electronDownload from 'electron-dl';
|
||||||
|
|
||||||
import createLoginWindow from './components/login/loginWindow';
|
import createLoginWindow from './components/login/loginWindow';
|
||||||
import createMainWindow from './components/mainWindow/mainWindow';
|
import createMainWindow from './components/mainWindow/mainWindow';
|
||||||
import helpers from './helpers/helpers';
|
import helpers from './helpers/helpers';
|
||||||
import inferFlash from './helpers/inferFlash';
|
import inferFlash from './helpers/inferFlash';
|
||||||
import electronDownload from 'electron-dl';
|
|
||||||
|
|
||||||
const {isOSX} = helpers;
|
const { isOSX } = helpers;
|
||||||
|
|
||||||
electronDownload();
|
electronDownload();
|
||||||
|
|
||||||
@ -18,83 +19,82 @@ const appArgs = JSON.parse(fs.readFileSync(APP_ARGS_FILE_PATH, 'utf8'));
|
|||||||
let mainWindow;
|
let mainWindow;
|
||||||
|
|
||||||
if (typeof appArgs.flashPluginDir === 'string') {
|
if (typeof appArgs.flashPluginDir === 'string') {
|
||||||
app.commandLine.appendSwitch('ppapi-flash-path', appArgs.flashPluginDir);
|
app.commandLine.appendSwitch('ppapi-flash-path', appArgs.flashPluginDir);
|
||||||
} else if (appArgs.flashPluginDir) {
|
} else if (appArgs.flashPluginDir) {
|
||||||
const flashPath = inferFlash();
|
const flashPath = inferFlash();
|
||||||
app.commandLine.appendSwitch('ppapi-flash-path', flashPath);
|
app.commandLine.appendSwitch('ppapi-flash-path', flashPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appArgs.ignoreCertificate) {
|
if (appArgs.ignoreCertificate) {
|
||||||
app.commandLine.appendSwitch('ignore-certificate-errors');
|
app.commandLine.appendSwitch('ignore-certificate-errors');
|
||||||
}
|
}
|
||||||
|
|
||||||
// do nothing for setDockBadge if not OSX
|
// do nothing for setDockBadge if not OSX
|
||||||
let setDockBadge = () => {};
|
let setDockBadge = () => {};
|
||||||
|
|
||||||
if (isOSX()) {
|
if (isOSX()) {
|
||||||
setDockBadge = app.dock.setBadge;
|
setDockBadge = app.dock.setBadge;
|
||||||
}
|
}
|
||||||
|
|
||||||
app.on('window-all-closed', () => {
|
app.on('window-all-closed', () => {
|
||||||
if (!isOSX() || appArgs.fastQuit) {
|
if (!isOSX() || appArgs.fastQuit) {
|
||||||
app.quit();
|
app.quit();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.on('activate', (event, hasVisibleWindows) => {
|
app.on('activate', (event, hasVisibleWindows) => {
|
||||||
if (isOSX()) {
|
if (isOSX()) {
|
||||||
// this is called when the dock is clicked
|
// this is called when the dock is clicked
|
||||||
if (!hasVisibleWindows) {
|
if (!hasVisibleWindows) {
|
||||||
mainWindow.show();
|
mainWindow.show();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.on('before-quit', () => {
|
app.on('before-quit', () => {
|
||||||
// not fired when the close button on the window is clicked
|
// not fired when the close button on the window is clicked
|
||||||
if (isOSX()) {
|
if (isOSX()) {
|
||||||
// need to force a quit as a workaround here to simulate the osx app hiding behaviour
|
// need to force a quit as a workaround here to simulate the osx app hiding behaviour
|
||||||
// Somehow sokution at https://github.com/atom/electron/issues/444#issuecomment-76492576 does not work,
|
// Somehow sokution at https://github.com/atom/electron/issues/444#issuecomment-76492576 does not work,
|
||||||
// e.prevent default appears to persist
|
// e.prevent default appears to persist
|
||||||
|
|
||||||
// might cause issues in the future as before-quit and will-quit events are not called
|
// might cause issues in the future as before-quit and will-quit events are not called
|
||||||
app.exit(0);
|
app.exit(0);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (appArgs.crashReporter) {
|
if (appArgs.crashReporter) {
|
||||||
app.on('will-finish-launching', () => {
|
app.on('will-finish-launching', () => {
|
||||||
crashReporter.start({
|
crashReporter.start({
|
||||||
productName: appArgs.name,
|
productName: appArgs.name,
|
||||||
submitURL: appArgs.crashReporter,
|
submitURL: appArgs.crashReporter,
|
||||||
autoSubmit: true
|
autoSubmit: true,
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
app.on('ready', () => {
|
app.on('ready', () => {
|
||||||
mainWindow = createMainWindow(appArgs, app.quit, setDockBadge);
|
mainWindow = createMainWindow(appArgs, app.quit, setDockBadge);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.on('login', (event, webContents, request, authInfo, callback) => {
|
app.on('login', (event, webContents, request, authInfo, callback) => {
|
||||||
// for http authentication
|
// for http authentication
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
createLoginWindow(callback);
|
createLoginWindow(callback);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (appArgs.singleInstance) {
|
if (appArgs.singleInstance) {
|
||||||
const shouldQuit = app.makeSingleInstance(() => {
|
const shouldQuit = app.makeSingleInstance(() => {
|
||||||
// Someone tried to run a second instance, we should focus our window.
|
// Someone tried to run a second instance, we should focus our window.
|
||||||
if (mainWindow) {
|
if (mainWindow) {
|
||||||
if (mainWindow.isMinimized()) {
|
if (mainWindow.isMinimized()) {
|
||||||
mainWindow.restore();
|
mainWindow.restore();
|
||||||
}
|
}
|
||||||
mainWindow.focus();
|
mainWindow.focus();
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (shouldQuit) {
|
|
||||||
app.quit();
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (shouldQuit) {
|
||||||
|
app.quit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
2
app/src/static/.eslintrc.yml
Normal file
2
app/src/static/.eslintrc.yml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
env:
|
||||||
|
browser: true
|
@ -1,11 +1,12 @@
|
|||||||
import electron from 'electron';
|
import electron from 'electron';
|
||||||
const {ipcRenderer} = electron;
|
|
||||||
|
const { ipcRenderer } = electron;
|
||||||
|
|
||||||
const form = document.getElementById('login-form');
|
const form = document.getElementById('login-form');
|
||||||
|
|
||||||
form.addEventListener('submit', event => {
|
form.addEventListener('submit', (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const username = document.getElementById('username-input').value;
|
const username = document.getElementById('username-input').value;
|
||||||
const password = document.getElementById('password-input').value;
|
const password = document.getElementById('password-input').value;
|
||||||
ipcRenderer.send('login-message', [username, password]);
|
ipcRenderer.send('login-message', [username, password]);
|
||||||
});
|
});
|
||||||
|
@ -1,86 +1,85 @@
|
|||||||
/**
|
/**
|
||||||
Preload file that will be executed in the renderer process
|
Preload file that will be executed in the renderer process
|
||||||
*/
|
*/
|
||||||
import {ipcRenderer, webFrame} from 'electron';
|
import { ipcRenderer, webFrame } from 'electron';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
|
||||||
const INJECT_JS_PATH = path.join(__dirname, '../../', 'inject/inject.js');
|
const INJECT_JS_PATH = path.join(__dirname, '../../', 'inject/inject.js');
|
||||||
|
|
||||||
setNotificationCallback((title, opt) => {
|
|
||||||
ipcRenderer.send('notification', title, opt);
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
// do things
|
|
||||||
|
|
||||||
window.addEventListener('contextmenu', event => {
|
|
||||||
event.preventDefault();
|
|
||||||
let targetElement = event.srcElement;
|
|
||||||
|
|
||||||
// the clicked element is the deepest in the DOM, and may not be the <a> bearing the href
|
|
||||||
// for example, <a href="..."><span>Google</span></a>
|
|
||||||
while (!targetElement.href && targetElement.parentElement) {
|
|
||||||
targetElement = targetElement.parentElement;
|
|
||||||
}
|
|
||||||
const targetHref = targetElement.href;
|
|
||||||
|
|
||||||
if (!targetHref) {
|
|
||||||
ipcRenderer.once('contextMenuClosed', () => {
|
|
||||||
clickSelector(event.target);
|
|
||||||
ipcRenderer.send('cancelNewWindowOverride');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ipcRenderer.send('contextMenuOpened', targetHref);
|
|
||||||
}, false);
|
|
||||||
|
|
||||||
injectScripts();
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcRenderer.on('params', (event, message) => {
|
|
||||||
const appArgs = JSON.parse(message);
|
|
||||||
console.log('nativefier.json', appArgs);
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcRenderer.on('debug', (event, message) => {
|
|
||||||
console.log('debug:', message);
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcRenderer.on('change-zoom', (event, message) => {
|
|
||||||
webFrame.setZoomFactor(message);
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Patches window.Notification to set a callback on a new Notification
|
* Patches window.Notification to set a callback on a new Notification
|
||||||
* @param callback
|
* @param callback
|
||||||
*/
|
*/
|
||||||
function setNotificationCallback(callback) {
|
function setNotificationCallback(callback) {
|
||||||
|
const OldNotify = window.Notification;
|
||||||
|
const newNotify = (title, opt) => {
|
||||||
|
callback(title, opt);
|
||||||
|
return new OldNotify(title, opt);
|
||||||
|
};
|
||||||
|
newNotify.requestPermission = OldNotify.requestPermission.bind(OldNotify);
|
||||||
|
Object.defineProperty(newNotify, 'permission', {
|
||||||
|
get: () => OldNotify.permission,
|
||||||
|
});
|
||||||
|
|
||||||
const OldNotify = window.Notification;
|
window.Notification = newNotify;
|
||||||
const newNotify = (title, opt) => {
|
|
||||||
callback(title, opt);
|
|
||||||
return new OldNotify(title, opt);
|
|
||||||
};
|
|
||||||
newNotify.requestPermission = OldNotify.requestPermission.bind(OldNotify);
|
|
||||||
Object.defineProperty(newNotify, 'permission', {
|
|
||||||
get: () => {
|
|
||||||
return OldNotify.permission;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
window.Notification = newNotify;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function clickSelector(element) {
|
function clickSelector(element) {
|
||||||
const mouseEvent = new MouseEvent('click');
|
const mouseEvent = new MouseEvent('click');
|
||||||
element.dispatchEvent(mouseEvent);
|
element.dispatchEvent(mouseEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
function injectScripts() {
|
function injectScripts() {
|
||||||
const needToInject = fs.existsSync(INJECT_JS_PATH);
|
const needToInject = fs.existsSync(INJECT_JS_PATH);
|
||||||
if (!needToInject) {
|
if (!needToInject) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
require(INJECT_JS_PATH);
|
// Dynamically require scripts
|
||||||
|
// eslint-disable-next-line global-require, import/no-dynamic-require
|
||||||
|
require(INJECT_JS_PATH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setNotificationCallback((title, opt) => {
|
||||||
|
ipcRenderer.send('notification', title, opt);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
window.addEventListener('contextmenu', (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
let targetElement = event.srcElement;
|
||||||
|
|
||||||
|
// the clicked element is the deepest in the DOM, and may not be the <a> bearing the href
|
||||||
|
// for example, <a href="..."><span>Google</span></a>
|
||||||
|
while (!targetElement.href && targetElement.parentElement) {
|
||||||
|
targetElement = targetElement.parentElement;
|
||||||
|
}
|
||||||
|
const targetHref = targetElement.href;
|
||||||
|
|
||||||
|
if (!targetHref) {
|
||||||
|
ipcRenderer.once('contextMenuClosed', () => {
|
||||||
|
clickSelector(event.target);
|
||||||
|
ipcRenderer.send('cancelNewWindowOverride');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcRenderer.send('contextMenuOpened', targetHref);
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
injectScripts();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcRenderer.on('params', (event, message) => {
|
||||||
|
const appArgs = JSON.parse(message);
|
||||||
|
console.log('nativefier.json', appArgs);
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcRenderer.on('debug', (event, message) => {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log('debug:', message);
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcRenderer.on('change-zoom', (event, message) => {
|
||||||
|
webFrame.setZoomFactor(message);
|
||||||
|
});
|
||||||
|
|
||||||
|
@ -1,19 +1,18 @@
|
|||||||
import gulp from 'gulp';
|
import gulp from 'gulp';
|
||||||
import PATHS from './helpers/src-paths';
|
|
||||||
|
|
||||||
import del from 'del';
|
import del from 'del';
|
||||||
import runSequence from 'run-sequence';
|
import runSequence from 'run-sequence';
|
||||||
|
import PATHS from './helpers/src-paths';
|
||||||
|
|
||||||
gulp.task('build', callback => {
|
gulp.task('build', (callback) => {
|
||||||
runSequence('clean', ['build-cli', 'build-app', 'build-tests'], callback);
|
runSequence('clean', ['build-cli', 'build-app', 'build-tests'], callback);
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('clean', callback => {
|
gulp.task('clean', (callback) => {
|
||||||
del(PATHS.CLI_DEST).then(() => {
|
del(PATHS.CLI_DEST).then(() => {
|
||||||
del(PATHS.APP_DEST).then(() => {
|
del(PATHS.APP_DEST).then(() => {
|
||||||
del(PATHS.TEST_DEST).then(() => {
|
del(PATHS.TEST_DEST).then(() => {
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import gulp from 'gulp';
|
import gulp from 'gulp';
|
||||||
|
import webpack from 'webpack-stream';
|
||||||
import PATHS from './../helpers/src-paths';
|
import PATHS from './../helpers/src-paths';
|
||||||
|
|
||||||
import webpack from 'webpack-stream';
|
const webpackConfig = require('./../../webpack.config.js');
|
||||||
|
|
||||||
gulp.task('build-app', ['build-static'], () => {
|
gulp.task('build-app', ['build-static'], () => gulp.src(PATHS.APP_MAIN_JS)
|
||||||
return gulp.src(PATHS.APP_MAIN_JS)
|
.pipe(webpack(webpackConfig))
|
||||||
.pipe(webpack(require('./../../webpack.config.js')))
|
.pipe(gulp.dest(PATHS.APP_DEST)));
|
||||||
.pipe(gulp.dest(PATHS.APP_DEST));
|
|
||||||
});
|
|
||||||
|
@ -2,8 +2,6 @@ import gulp from 'gulp';
|
|||||||
import PATHS from './../helpers/src-paths';
|
import PATHS from './../helpers/src-paths';
|
||||||
import helpers from './../helpers/gulp-helpers';
|
import helpers from './../helpers/gulp-helpers';
|
||||||
|
|
||||||
const {buildES6} = helpers;
|
const { buildES6 } = helpers;
|
||||||
|
|
||||||
gulp.task('build-cli', done => {
|
gulp.task('build-cli', done => buildES6(PATHS.CLI_SRC_JS, PATHS.CLI_DEST, done));
|
||||||
return buildES6(PATHS.CLI_SRC_JS, PATHS.CLI_DEST, done);
|
|
||||||
});
|
|
||||||
|
@ -2,15 +2,11 @@ import gulp from 'gulp';
|
|||||||
import PATHS from './../helpers/src-paths';
|
import PATHS from './../helpers/src-paths';
|
||||||
import helpers from './../helpers/gulp-helpers';
|
import helpers from './../helpers/gulp-helpers';
|
||||||
|
|
||||||
const {buildES6} = helpers;
|
const { buildES6 } = helpers;
|
||||||
|
|
||||||
gulp.task('build-static-not-js', () => {
|
gulp.task('build-static-not-js', () => gulp.src([PATHS.APP_STATIC_ALL, '!**/*.js'])
|
||||||
return gulp.src([PATHS.APP_STATIC_ALL, '!**/*.js'])
|
.pipe(gulp.dest(PATHS.APP_STATIC_DEST)));
|
||||||
.pipe(gulp.dest(PATHS.APP_STATIC_DEST));
|
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task('build-static-js', done => {
|
gulp.task('build-static-js', done => buildES6(PATHS.APP_STATIC_JS, PATHS.APP_STATIC_DEST, done));
|
||||||
return buildES6(PATHS.APP_STATIC_JS, PATHS.APP_STATIC_DEST, done);
|
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task('build-static', ['build-static-js', 'build-static-not-js']);
|
gulp.task('build-static', ['build-static-js', 'build-static-not-js']);
|
||||||
|
@ -4,17 +4,17 @@ import sourcemaps from 'gulp-sourcemaps';
|
|||||||
import babel from 'gulp-babel';
|
import babel from 'gulp-babel';
|
||||||
|
|
||||||
function shellExec(cmd, silent, callback) {
|
function shellExec(cmd, silent, callback) {
|
||||||
shellJs.exec(cmd, {silent: silent}, (code, stdout, stderr) => {
|
shellJs.exec(cmd, { silent }, (code, stdout, stderr) => {
|
||||||
if (code) {
|
if (code) {
|
||||||
callback(JSON.stringify({code, stdout, stderr}));
|
callback(JSON.stringify({ code, stdout, stderr }));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildES6(src, dest, callback) {
|
function buildES6(src, dest, callback) {
|
||||||
return gulp.src(src)
|
return gulp.src(src)
|
||||||
.pipe(sourcemaps.init())
|
.pipe(sourcemaps.init())
|
||||||
.pipe(babel())
|
.pipe(babel())
|
||||||
.on('error', callback)
|
.on('error', callback)
|
||||||
@ -23,6 +23,6 @@ function buildES6(src, dest, callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
shellExec,
|
shellExec,
|
||||||
buildES6
|
buildES6,
|
||||||
};
|
};
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
const paths = {
|
const paths = {
|
||||||
APP_SRC: 'app/src',
|
APP_SRC: 'app/src',
|
||||||
APP_DEST: 'app/lib',
|
APP_DEST: 'app/lib',
|
||||||
CLI_SRC: 'src',
|
CLI_SRC: 'src',
|
||||||
CLI_DEST: 'lib',
|
CLI_DEST: 'lib',
|
||||||
TEST_SRC: 'test',
|
TEST_SRC: 'test',
|
||||||
TEST_DEST: 'built-tests'
|
TEST_DEST: 'built-tests',
|
||||||
};
|
};
|
||||||
|
|
||||||
paths.APP_MAIN_JS = path.join(paths.APP_SRC, '/main.js');
|
paths.APP_MAIN_JS = path.join(paths.APP_SRC, '/main.js');
|
||||||
paths.APP_ALL = paths.APP_SRC + '/**/*';
|
paths.APP_ALL = `${paths.APP_SRC}/**/*`;
|
||||||
paths.APP_STATIC_ALL = path.join(paths.APP_SRC, 'static') + '/**/*';
|
paths.APP_STATIC_ALL = `${path.join(paths.APP_SRC, 'static')}/**/*`;
|
||||||
paths.APP_STATIC_JS = path.join(paths.APP_SRC, 'static') + '/**/*.js';
|
paths.APP_STATIC_JS = `${path.join(paths.APP_SRC, 'static')}/**/*.js`;
|
||||||
paths.APP_STATIC_DEST = path.join(paths.APP_DEST, 'static');
|
paths.APP_STATIC_DEST = path.join(paths.APP_DEST, 'static');
|
||||||
paths.CLI_SRC_JS = paths.CLI_SRC + '/**/*.js';
|
paths.CLI_SRC_JS = `${paths.CLI_SRC}/**/*.js`;
|
||||||
paths.CLI_DEST_JS = paths.CLI_DEST + '/**/*.js';
|
paths.CLI_DEST_JS = `${paths.CLI_DEST}/**/*.js`;
|
||||||
paths.TEST_SRC_JS = paths.TEST_SRC + '/**/*.js';
|
paths.TEST_SRC_JS = `${paths.TEST_SRC}/**/*.js`;
|
||||||
paths.TEST_DEST_JS = paths.TEST_DEST + '/**/*.js';
|
paths.TEST_DEST_JS = `${paths.TEST_DEST}/**/*.js`;
|
||||||
|
|
||||||
export default paths;
|
export default paths;
|
||||||
|
@ -2,12 +2,10 @@ import gulp from 'gulp';
|
|||||||
import runSequence from 'run-sequence';
|
import runSequence from 'run-sequence';
|
||||||
import helpers from './helpers/gulp-helpers';
|
import helpers from './helpers/gulp-helpers';
|
||||||
|
|
||||||
const {shellExec} = helpers;
|
const { shellExec } = helpers;
|
||||||
|
|
||||||
gulp.task('publish', done => {
|
gulp.task('publish', (done) => {
|
||||||
shellExec('npm publish', false, done);
|
shellExec('npm publish', false, done);
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('release', callback => {
|
gulp.task('release', callback => runSequence('build', 'publish', callback));
|
||||||
return runSequence('build', 'publish', callback);
|
|
||||||
});
|
|
||||||
|
10
gulp/test.js
10
gulp/test.js
@ -2,12 +2,10 @@ import gulp from 'gulp';
|
|||||||
import runSequence from 'run-sequence';
|
import runSequence from 'run-sequence';
|
||||||
import helpers from './helpers/gulp-helpers';
|
import helpers from './helpers/gulp-helpers';
|
||||||
|
|
||||||
const {shellExec} = helpers;
|
const { shellExec } = helpers;
|
||||||
|
|
||||||
gulp.task('prune', done => {
|
gulp.task('prune', (done) => {
|
||||||
shellExec('npm prune', true, done);
|
shellExec('npm prune', true, done);
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('test', callback => {
|
gulp.task('test', callback => runSequence('prune', 'mocha', callback));
|
||||||
return runSequence('prune', 'mocha', callback);
|
|
||||||
});
|
|
||||||
|
@ -2,8 +2,6 @@ import gulp from 'gulp';
|
|||||||
import PATHS from './../helpers/src-paths';
|
import PATHS from './../helpers/src-paths';
|
||||||
import helpers from './../helpers/gulp-helpers';
|
import helpers from './../helpers/gulp-helpers';
|
||||||
|
|
||||||
const {buildES6} = helpers;
|
const { buildES6 } = helpers;
|
||||||
|
|
||||||
gulp.task('build-tests', done => {
|
gulp.task('build-tests', done => buildES6(PATHS.TEST_SRC_JS, PATHS.TEST_DEST, done));
|
||||||
return buildES6(PATHS.TEST_SRC_JS, PATHS.TEST_DEST, done);
|
|
||||||
});
|
|
||||||
|
@ -1,32 +1,27 @@
|
|||||||
import gulp from 'gulp';
|
import gulp from 'gulp';
|
||||||
|
import istanbul from 'gulp-istanbul';
|
||||||
|
import { Instrumenter } from 'isparta';
|
||||||
|
import mocha from 'gulp-mocha';
|
||||||
import PATHS from './../helpers/src-paths';
|
import PATHS from './../helpers/src-paths';
|
||||||
|
|
||||||
import istanbul from 'gulp-istanbul';
|
gulp.task('mocha', (done) => {
|
||||||
import {Instrumenter} from 'isparta';
|
gulp.src([PATHS.CLI_SRC_JS, '!src/cli.js'])
|
||||||
import mocha from 'gulp-mocha';
|
.pipe(istanbul({
|
||||||
|
instrumenter: Instrumenter,
|
||||||
gulp.task('mocha', done => {
|
includeUntested: true,
|
||||||
gulp.src([PATHS.CLI_SRC_JS, '!src/cli.js'])
|
}))
|
||||||
.pipe(istanbul({
|
.pipe(istanbul.hookRequire()) // Force `require` to return covered files
|
||||||
instrumenter: Instrumenter,
|
.on('finish', () => gulp.src(PATHS.TEST_SRC, { read: false })
|
||||||
includeUntested: true
|
.pipe(mocha({
|
||||||
}))
|
compilers: 'js:babel-core/register',
|
||||||
.pipe(istanbul.hookRequire()) // Force `require` to return covered files
|
recursive: true,
|
||||||
.on('finish', () => {
|
}))
|
||||||
return gulp.src(PATHS.TEST_SRC, {read: false})
|
.pipe(istanbul.writeReports({
|
||||||
.pipe(mocha({
|
dir: './coverage',
|
||||||
compilers: 'js:babel-core/register',
|
reporters: ['lcov'],
|
||||||
recursive: true
|
reportOpts: { dir: './coverage' },
|
||||||
}))
|
}))
|
||||||
.pipe(istanbul.writeReports({
|
.on('end', done));
|
||||||
dir: './coverage',
|
|
||||||
reporters: ['lcov'],
|
|
||||||
reportOpts: {dir: './coverage'}
|
|
||||||
}))
|
|
||||||
.on('end', done);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('tdd', ['mocha'], () => {
|
gulp.task('tdd', ['mocha'], () => gulp.watch(['src/**/*.js', 'test/**/*.js'], ['mocha']));
|
||||||
return gulp.watch(['src/**/*.js', 'test/**/*.js'], ['mocha']);
|
|
||||||
});
|
|
||||||
|
@ -2,15 +2,15 @@ import gulp from 'gulp';
|
|||||||
import PATHS from './helpers/src-paths';
|
import PATHS from './helpers/src-paths';
|
||||||
|
|
||||||
gulp.task('watch', ['build'], () => {
|
gulp.task('watch', ['build'], () => {
|
||||||
var handleError = function(error) {
|
const handleError = function (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
};
|
};
|
||||||
gulp.watch(PATHS.APP_ALL, ['build-app'])
|
gulp.watch(PATHS.APP_ALL, ['build-app'])
|
||||||
.on('error', handleError);
|
.on('error', handleError);
|
||||||
|
|
||||||
gulp.watch(PATHS.CLI_SRC_JS, ['build-cli'])
|
gulp.watch(PATHS.CLI_SRC_JS, ['build-cli'])
|
||||||
.on('error', handleError);
|
.on('error', handleError);
|
||||||
|
|
||||||
gulp.watch(PATHS.TEST_SRC_JS, ['build-tests'])
|
gulp.watch(PATHS.TEST_SRC_JS, ['build-tests'])
|
||||||
.on('error', handleError);
|
.on('error', handleError);
|
||||||
});
|
});
|
||||||
|
@ -2,8 +2,8 @@ import gulp from 'gulp';
|
|||||||
import requireDir from 'require-dir';
|
import requireDir from 'require-dir';
|
||||||
|
|
||||||
requireDir('./gulp', {
|
requireDir('./gulp', {
|
||||||
recurse: true,
|
recurse: true,
|
||||||
duplicates: true
|
duplicates: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('default', ['build']);
|
gulp.task('default', ['build']);
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
"test": "gulp test",
|
"test": "gulp test",
|
||||||
"tdd": "gulp tdd",
|
"tdd": "gulp tdd",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
|
"lint:fix": "eslint . --fix",
|
||||||
"ci": "gulp build test && npm run lint",
|
"ci": "gulp build test && npm run lint",
|
||||||
"clean": "gulp clean",
|
"clean": "gulp clean",
|
||||||
"build": "gulp build",
|
"build": "gulp build",
|
||||||
@ -68,8 +69,9 @@
|
|||||||
"babel-register": "^6.6.0",
|
"babel-register": "^6.6.0",
|
||||||
"chai": "^3.4.1",
|
"chai": "^3.4.1",
|
||||||
"del": "^2.2.0",
|
"del": "^2.2.0",
|
||||||
"eslint": "^2.10.2",
|
"eslint": "^3.19.0",
|
||||||
"eslint-config-google": "^0.5.0",
|
"eslint-config-airbnb-base": "^11.1.3",
|
||||||
|
"eslint-plugin-import": "^2.2.0",
|
||||||
"gulp": "^3.9.0",
|
"gulp": "^3.9.0",
|
||||||
"gulp-babel": "^6.1.1",
|
"gulp-babel": "^6.1.1",
|
||||||
"gulp-istanbul": "^1.1.1",
|
"gulp-istanbul": "^1.1.1",
|
||||||
|
@ -7,8 +7,100 @@ import ncp from 'ncp';
|
|||||||
const copy = ncp.ncp;
|
const copy = ncp.ncp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a temporary directory and copies the './app folder' inside, and adds a text file with the configuration
|
* Only picks certain app args to pass to nativefier.json
|
||||||
* for the single page app.
|
* @param options
|
||||||
|
*/
|
||||||
|
function selectAppArgs(options) {
|
||||||
|
return {
|
||||||
|
name: options.name,
|
||||||
|
targetUrl: options.targetUrl,
|
||||||
|
counter: options.counter,
|
||||||
|
width: options.width,
|
||||||
|
height: options.height,
|
||||||
|
minWidth: options.minWidth,
|
||||||
|
minHeight: options.minHeight,
|
||||||
|
maxWidth: options.maxWidth,
|
||||||
|
maxHeight: options.maxHeight,
|
||||||
|
showMenuBar: options.showMenuBar,
|
||||||
|
fastQuit: options.fastQuit,
|
||||||
|
userAgent: options.userAgent,
|
||||||
|
nativefierVersion: options.nativefierVersion,
|
||||||
|
ignoreCertificate: options.ignoreCertificate,
|
||||||
|
insecure: options.insecure,
|
||||||
|
flashPluginDir: options.flashPluginDir,
|
||||||
|
fullScreen: options.fullScreen,
|
||||||
|
hideWindowFrame: options.hideWindowFrame,
|
||||||
|
maximize: options.maximize,
|
||||||
|
disableContextMenu: options.disableContextMenu,
|
||||||
|
disableDevTools: options.disableDevTools,
|
||||||
|
zoom: options.zoom,
|
||||||
|
internalUrls: options.internalUrls,
|
||||||
|
crashReporter: options.crashReporter,
|
||||||
|
singleInstance: options.singleInstance,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function maybeCopyScripts(srcs, dest) {
|
||||||
|
if (!srcs) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const promises = srcs.map(src => new Promise((resolve, reject) => {
|
||||||
|
if (!fs.existsSync(src)) {
|
||||||
|
reject('Error copying injection files: file not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let destFileName;
|
||||||
|
if (path.extname(src) === '.js') {
|
||||||
|
destFileName = 'inject.js';
|
||||||
|
} else if (path.extname(src) === '.css') {
|
||||||
|
destFileName = 'inject.css';
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(src, path.join(dest, 'inject', destFileName), (error) => {
|
||||||
|
if (error) {
|
||||||
|
reject(`Error Copying injection files: ${error}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
Promise.all(promises)
|
||||||
|
.then(() => {
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeAppName(appName, url) {
|
||||||
|
// use a simple 3 byte random string to prevent collision
|
||||||
|
const hash = crypto.createHash('md5');
|
||||||
|
hash.update(url);
|
||||||
|
const postFixHash = hash.digest('hex').substring(0, 6);
|
||||||
|
const normalized = _.kebabCase(appName.toLowerCase());
|
||||||
|
return `${normalized}-nativefier-${postFixHash}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeAppPackageJsonName(appPath, name, url) {
|
||||||
|
const packageJsonPath = path.join(appPath, '/package.json');
|
||||||
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath));
|
||||||
|
packageJson.name = normalizeAppName(name, url);
|
||||||
|
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a temporary directory and copies the './app folder' inside,
|
||||||
|
* and adds a text file with the configuration for the single page app.
|
||||||
*
|
*
|
||||||
* @param {string} src
|
* @param {string} src
|
||||||
* @param {string} dest
|
* @param {string} dest
|
||||||
@ -16,119 +108,25 @@ const copy = ncp.ncp;
|
|||||||
* @param callback
|
* @param callback
|
||||||
*/
|
*/
|
||||||
function buildApp(src, dest, options, callback) {
|
function buildApp(src, dest, options, callback) {
|
||||||
const appArgs = selectAppArgs(options);
|
const appArgs = selectAppArgs(options);
|
||||||
copy(src, dest, error => {
|
copy(src, dest, (error) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
callback(`Error Copying temporary directory: ${error}`);
|
callback(`Error Copying temporary directory: ${error}`);
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
fs.writeFileSync(path.join(dest, '/nativefier.json'), JSON.stringify(appArgs));
|
|
||||||
|
|
||||||
maybeCopyScripts(options.inject, dest)
|
|
||||||
.catch(error => {
|
|
||||||
console.warn(error);
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
changeAppPackageJsonName(dest, appArgs.name, appArgs.targetUrl);
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function maybeCopyScripts(srcs, dest) {
|
|
||||||
if (!srcs) {
|
|
||||||
return new Promise(resolve => {
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
const promises = srcs.map(src => {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
if (!fs.existsSync(src)) {
|
|
||||||
reject('Error copying injection files: file not found');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let destFileName;
|
fs.writeFileSync(path.join(dest, '/nativefier.json'), JSON.stringify(appArgs));
|
||||||
if (path.extname(src) === '.js') {
|
|
||||||
destFileName = 'inject.js';
|
|
||||||
} else if (path.extname(src) === '.css') {
|
|
||||||
destFileName = 'inject.css';
|
|
||||||
} else {
|
|
||||||
resolve();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
copy(src, path.join(dest, 'inject', destFileName), error => {
|
maybeCopyScripts(options.inject, dest)
|
||||||
if (error) {
|
.catch((error) => {
|
||||||
reject(`Error Copying injection files: ${error}`);
|
console.warn(error);
|
||||||
return;
|
})
|
||||||
}
|
.then(() => {
|
||||||
resolve();
|
changeAppPackageJsonName(dest, appArgs.name, appArgs.targetUrl);
|
||||||
});
|
callback();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
Promise.all(promises)
|
|
||||||
.then(() => {
|
|
||||||
resolve();
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
reject(error);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeAppPackageJsonName(appPath, name, url) {
|
|
||||||
const packageJsonPath = path.join(appPath, '/package.json');
|
|
||||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath));
|
|
||||||
packageJson.name = normalizeAppName(name, url);
|
|
||||||
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Only picks certain app args to pass to nativefier.json
|
|
||||||
* @param options
|
|
||||||
* @returns {{name: (*|string), targetUrl: (string|*), counter: *, width: *, height: *, showMenuBar: *, userAgent: *, nativefierVersion: *, insecure: *, disableWebSecurity: *}}
|
|
||||||
*/
|
|
||||||
function selectAppArgs(options) {
|
|
||||||
return {
|
|
||||||
name: options.name,
|
|
||||||
targetUrl: options.targetUrl,
|
|
||||||
counter: options.counter,
|
|
||||||
width: options.width,
|
|
||||||
height: options.height,
|
|
||||||
minWidth: options.minWidth,
|
|
||||||
minHeight: options.minHeight,
|
|
||||||
maxWidth: options.maxWidth,
|
|
||||||
maxHeight: options.maxHeight,
|
|
||||||
showMenuBar: options.showMenuBar,
|
|
||||||
fastQuit: options.fastQuit,
|
|
||||||
userAgent: options.userAgent,
|
|
||||||
nativefierVersion: options.nativefierVersion,
|
|
||||||
ignoreCertificate: options.ignoreCertificate,
|
|
||||||
insecure: options.insecure,
|
|
||||||
flashPluginDir: options.flashPluginDir,
|
|
||||||
fullScreen: options.fullScreen,
|
|
||||||
hideWindowFrame: options.hideWindowFrame,
|
|
||||||
maximize: options.maximize,
|
|
||||||
disableContextMenu: options.disableContextMenu,
|
|
||||||
disableDevTools: options.disableDevTools,
|
|
||||||
zoom: options.zoom,
|
|
||||||
internalUrls: options.internalUrls,
|
|
||||||
crashReporter: options.crashReporter,
|
|
||||||
singleInstance: options.singleInstance
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeAppName(appName, url) {
|
|
||||||
// use a simple 3 byte random string to prevent collision
|
|
||||||
let hash = crypto.createHash('md5');
|
|
||||||
hash.update(url);
|
|
||||||
const postFixHash = hash.digest('hex').substring(0, 6);
|
|
||||||
const normalized = _.kebabCase(appName.toLowerCase());
|
|
||||||
return `${normalized}-nativefier-${postFixHash}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default buildApp;
|
export default buildApp;
|
||||||
|
@ -15,118 +15,39 @@ import buildApp from './buildApp';
|
|||||||
const copy = ncp.ncp;
|
const copy = ncp.ncp;
|
||||||
const isWindows = helpers.isWindows;
|
const isWindows = helpers.isWindows;
|
||||||
|
|
||||||
/**
|
|
||||||
* @callback buildAppCallback
|
|
||||||
* @param error
|
|
||||||
* @param {string} appPath
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {{}} options
|
|
||||||
* @param {buildAppCallback} callback
|
|
||||||
*/
|
|
||||||
function buildMain(options, callback) {
|
|
||||||
// pre process app
|
|
||||||
|
|
||||||
const tmpObj = tmp.dirSync({unsafeCleanup: true});
|
|
||||||
const tmpPath = tmpObj.name;
|
|
||||||
|
|
||||||
// todo check if this is still needed on later version of packager
|
|
||||||
const packagerConsole = new PackagerConsole();
|
|
||||||
|
|
||||||
const progress = new DishonestProgress(5);
|
|
||||||
|
|
||||||
async.waterfall([
|
|
||||||
callback => {
|
|
||||||
progress.tick('inferring');
|
|
||||||
optionsFactory(options, callback);
|
|
||||||
},
|
|
||||||
(options, callback) => {
|
|
||||||
progress.tick('copying');
|
|
||||||
buildApp(options.dir, tmpPath, options, error => {
|
|
||||||
if (error) {
|
|
||||||
callback(error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// dir now correctly references the app folder to package
|
|
||||||
options.dir = tmpPath;
|
|
||||||
callback(null, options);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
(options, callback) => {
|
|
||||||
progress.tick('icons');
|
|
||||||
iconBuild(options, (error, optionsWithIcon) => {
|
|
||||||
callback(null, optionsWithIcon);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
(options, callback) => {
|
|
||||||
progress.tick('packaging');
|
|
||||||
// maybe skip passing icon parameter to electron packager
|
|
||||||
const packageOptions = maybeNoIconOption(options);
|
|
||||||
|
|
||||||
packagerConsole.override();
|
|
||||||
|
|
||||||
packager(packageOptions, (error, appPathArray) => {
|
|
||||||
|
|
||||||
// restore console.error
|
|
||||||
packagerConsole.restore();
|
|
||||||
|
|
||||||
// pass options which still contains the icon to waterfall
|
|
||||||
callback(error, options, appPathArray);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
(options, appPathArray, callback) => {
|
|
||||||
progress.tick('finalizing');
|
|
||||||
// somehow appPathArray is a 1 element array
|
|
||||||
const appPath = getAppPath(appPathArray);
|
|
||||||
if (!appPath) {
|
|
||||||
callback();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
maybeCopyIcons(options, appPath, error => {
|
|
||||||
callback(error, appPath);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
], (error, appPath) => {
|
|
||||||
packagerConsole.playback();
|
|
||||||
callback(error, appPath);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks the app path array to determine if the packaging was completed successfully
|
* Checks the app path array to determine if the packaging was completed successfully
|
||||||
* @param appPathArray Result from electron-packager
|
* @param appPathArray Result from electron-packager
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
function getAppPath(appPathArray) {
|
function getAppPath(appPathArray) {
|
||||||
if (appPathArray.length === 0) {
|
if (appPathArray.length === 0) {
|
||||||
// directory already exists, --overwrite is not set
|
// directory already exists, --overwrite is not set
|
||||||
// exit here
|
// exit here
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appPathArray.length > 1) {
|
if (appPathArray.length > 1) {
|
||||||
log.warn('Warning: This should not be happening, packaged app path contains more than one element:', appPathArray);
|
log.warn('Warning: This should not be happening, packaged app path contains more than one element:', appPathArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
return appPathArray[0];
|
return appPathArray[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the `icon` parameter from options if building for Windows while not on Windows and Wine is not installed
|
* Removes the `icon` parameter from options if building for Windows while not on Windows
|
||||||
|
* and Wine is not installed
|
||||||
* @param options
|
* @param options
|
||||||
*/
|
*/
|
||||||
function maybeNoIconOption(options) {
|
function maybeNoIconOption(options) {
|
||||||
const packageOptions = JSON.parse(JSON.stringify(options));
|
const packageOptions = JSON.parse(JSON.stringify(options));
|
||||||
if (options.platform === 'win32' && !isWindows()) {
|
if (options.platform === 'win32' && !isWindows()) {
|
||||||
if (!hasBinary.sync('wine')) {
|
if (!hasBinary.sync('wine')) {
|
||||||
log.warn('Wine is required to set the icon for a Windows app when packaging on non-windows platforms');
|
log.warn('Wine is required to set the icon for a Windows app when packaging on non-windows platforms');
|
||||||
packageOptions.icon = null;
|
packageOptions.icon = null;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return packageOptions;
|
}
|
||||||
|
return packageOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -137,23 +58,105 @@ function maybeNoIconOption(options) {
|
|||||||
* @param callback
|
* @param callback
|
||||||
*/
|
*/
|
||||||
function maybeCopyIcons(options, appPath, callback) {
|
function maybeCopyIcons(options, appPath, callback) {
|
||||||
if (!options.icon) {
|
if (!options.icon) {
|
||||||
|
callback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.platform === 'darwin') {
|
||||||
|
callback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// windows & linux
|
||||||
|
// put the icon file into the app
|
||||||
|
const destIconPath = path.join(appPath, 'resources/app');
|
||||||
|
const destFileName = `icon${path.extname(options.icon)}`;
|
||||||
|
copy(options.icon, path.join(destIconPath, destFileName), (error) => {
|
||||||
|
callback(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @callback buildAppCallback
|
||||||
|
* @param error
|
||||||
|
* @param {string} appPath
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {{}} inpOptions
|
||||||
|
* @param {buildAppCallback} callback
|
||||||
|
*/
|
||||||
|
function buildMain(inpOptions, callback) {
|
||||||
|
const options = Object.assign({}, inpOptions);
|
||||||
|
|
||||||
|
// pre process app
|
||||||
|
|
||||||
|
const tmpObj = tmp.dirSync({ unsafeCleanup: true });
|
||||||
|
const tmpPath = tmpObj.name;
|
||||||
|
|
||||||
|
// todo check if this is still needed on later version of packager
|
||||||
|
const packagerConsole = new PackagerConsole();
|
||||||
|
|
||||||
|
const progress = new DishonestProgress(5);
|
||||||
|
|
||||||
|
async.waterfall([
|
||||||
|
(callback) => {
|
||||||
|
progress.tick('inferring');
|
||||||
|
optionsFactory(options, callback);
|
||||||
|
},
|
||||||
|
(options, callback) => {
|
||||||
|
progress.tick('copying');
|
||||||
|
buildApp(options.dir, tmpPath, options, (error) => {
|
||||||
|
if (error) {
|
||||||
|
callback(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Change the reference file for the Electron app to be the temporary path
|
||||||
|
const newOptions = Object.assign({}, options, { dir: tmpPath });
|
||||||
|
callback(null, newOptions);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
(options, callback) => {
|
||||||
|
progress.tick('icons');
|
||||||
|
iconBuild(options, (error, optionsWithIcon) => {
|
||||||
|
callback(null, optionsWithIcon);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
(options, callback) => {
|
||||||
|
progress.tick('packaging');
|
||||||
|
// maybe skip passing icon parameter to electron packager
|
||||||
|
const packageOptions = maybeNoIconOption(options);
|
||||||
|
|
||||||
|
packagerConsole.override();
|
||||||
|
|
||||||
|
packager(packageOptions, (error, appPathArray) => {
|
||||||
|
// restore console.error
|
||||||
|
packagerConsole.restore();
|
||||||
|
|
||||||
|
// pass options which still contains the icon to waterfall
|
||||||
|
callback(error, options, appPathArray);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
(options, appPathArray, callback) => {
|
||||||
|
progress.tick('finalizing');
|
||||||
|
// somehow appPathArray is a 1 element array
|
||||||
|
const appPath = getAppPath(appPathArray);
|
||||||
|
if (!appPath) {
|
||||||
callback();
|
callback();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.platform === 'darwin') {
|
maybeCopyIcons(options, appPath, (error) => {
|
||||||
callback();
|
callback(error, appPath);
|
||||||
return;
|
});
|
||||||
}
|
},
|
||||||
|
], (error, appPath) => {
|
||||||
// windows & linux
|
packagerConsole.playback();
|
||||||
// put the icon file into the app
|
callback(error, appPath);
|
||||||
const destIconPath = path.join(appPath, 'resources/app');
|
});
|
||||||
const destFileName = `icon${path.extname(options.icon)}`;
|
|
||||||
copy(options.icon, path.join(destIconPath, destFileName), error => {
|
|
||||||
callback(error);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default buildMain;
|
export default buildMain;
|
||||||
|
@ -3,8 +3,20 @@ import log from 'loglevel';
|
|||||||
import helpers from './../helpers/helpers';
|
import helpers from './../helpers/helpers';
|
||||||
import iconShellHelpers from './../helpers/iconShellHelpers';
|
import iconShellHelpers from './../helpers/iconShellHelpers';
|
||||||
|
|
||||||
const {isOSX} = helpers;
|
const { isOSX } = helpers;
|
||||||
const {convertToPng, convertToIco, convertToIcns} = iconShellHelpers;
|
const { convertToPng, convertToIco, convertToIcns } = iconShellHelpers;
|
||||||
|
|
||||||
|
function iconIsIco(iconPath) {
|
||||||
|
return path.extname(iconPath) === '.ico';
|
||||||
|
}
|
||||||
|
|
||||||
|
function iconIsPng(iconPath) {
|
||||||
|
return path.extname(iconPath) === '.png';
|
||||||
|
}
|
||||||
|
|
||||||
|
function iconIsIcns(iconPath) {
|
||||||
|
return path.extname(iconPath) === '.icns';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @callback augmentIconsCallback
|
* @callback augmentIconsCallback
|
||||||
@ -16,88 +28,76 @@ const {convertToPng, convertToIco, convertToIcns} = iconShellHelpers;
|
|||||||
* Will check and convert a `.png` to `.icns` if necessary and augment
|
* Will check and convert a `.png` to `.icns` if necessary and augment
|
||||||
* options.icon with the result
|
* options.icon with the result
|
||||||
*
|
*
|
||||||
* @param options will need options.platform and options.icon
|
* @param inpOptions will need options.platform and options.icon
|
||||||
* @param {augmentIconsCallback} callback
|
* @param {augmentIconsCallback} callback
|
||||||
*/
|
*/
|
||||||
function iconBuild(options, callback) {
|
function iconBuild(inpOptions, callback) {
|
||||||
|
const options = Object.assign({}, inpOptions);
|
||||||
|
const returnCallback = () => {
|
||||||
|
callback(null, options);
|
||||||
|
};
|
||||||
|
|
||||||
const returnCallback = () => {
|
if (!options.icon) {
|
||||||
callback(null, options);
|
returnCallback();
|
||||||
};
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!options.icon) {
|
if (options.platform === 'win32') {
|
||||||
|
if (iconIsIco(options.icon)) {
|
||||||
|
returnCallback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
convertToIco(options.icon)
|
||||||
|
.then((outPath) => {
|
||||||
|
options.icon = outPath;
|
||||||
returnCallback();
|
returnCallback();
|
||||||
return;
|
})
|
||||||
}
|
.catch((error) => {
|
||||||
|
log.warn('Skipping icon conversion to .ico', error);
|
||||||
if (options.platform === 'win32') {
|
|
||||||
if (iconIsIco(options.icon)) {
|
|
||||||
returnCallback();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
convertToIco(options.icon)
|
|
||||||
.then(outPath => {
|
|
||||||
options.icon = outPath;
|
|
||||||
returnCallback();
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
log.warn('Skipping icon conversion to .ico', error);
|
|
||||||
returnCallback();
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.platform === 'linux') {
|
|
||||||
if (iconIsPng(options.icon)) {
|
|
||||||
returnCallback();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
convertToPng(options.icon)
|
|
||||||
.then(outPath => {
|
|
||||||
options.icon = outPath;
|
|
||||||
returnCallback();
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
log.warn('Skipping icon conversion to .png', error);
|
|
||||||
returnCallback();
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iconIsIcns(options.icon)) {
|
|
||||||
returnCallback();
|
returnCallback();
|
||||||
return;
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.platform === 'linux') {
|
||||||
|
if (iconIsPng(options.icon)) {
|
||||||
|
returnCallback();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isOSX()) {
|
convertToPng(options.icon)
|
||||||
log.warn('Skipping icon conversion to .icns, conversion is only supported on OSX');
|
.then((outPath) => {
|
||||||
|
options.icon = outPath;
|
||||||
returnCallback();
|
returnCallback();
|
||||||
return;
|
})
|
||||||
}
|
.catch((error) => {
|
||||||
|
log.warn('Skipping icon conversion to .png', error);
|
||||||
|
returnCallback();
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
convertToIcns(options.icon)
|
if (iconIsIcns(options.icon)) {
|
||||||
.then(outPath => {
|
returnCallback();
|
||||||
options.icon = outPath;
|
return;
|
||||||
returnCallback();
|
}
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
log.warn('Skipping icon conversion to .icns', error);
|
|
||||||
returnCallback();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function iconIsIco(iconPath) {
|
if (!isOSX()) {
|
||||||
return path.extname(iconPath) === '.ico';
|
log.warn('Skipping icon conversion to .icns, conversion is only supported on OSX');
|
||||||
}
|
returnCallback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
function iconIsPng(iconPath) {
|
convertToIcns(options.icon)
|
||||||
return path.extname(iconPath) === '.png';
|
.then((outPath) => {
|
||||||
}
|
options.icon = outPath;
|
||||||
|
returnCallback();
|
||||||
function iconIsIcns(iconPath) {
|
})
|
||||||
return path.extname(iconPath) === '.icns';
|
.catch((error) => {
|
||||||
|
log.warn('Skipping icon conversion to .icns', error);
|
||||||
|
returnCallback();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export default iconBuild;
|
export default iconBuild;
|
||||||
|
118
src/cli.js
118
src/cli.js
@ -1,75 +1,73 @@
|
|||||||
#! /usr/bin/env node
|
#! /usr/bin/env node
|
||||||
|
|
||||||
import 'source-map-support/register';
|
import 'source-map-support/register';
|
||||||
|
|
||||||
import path from 'path';
|
|
||||||
import program from 'commander';
|
import program from 'commander';
|
||||||
import nativefier from './index';
|
import nativefier from './index';
|
||||||
const packageJson = require(path.join('..', 'package'));
|
|
||||||
|
const packageJson = require('./../package');
|
||||||
|
|
||||||
function collect(val, memo) {
|
function collect(val, memo) {
|
||||||
memo.push(val);
|
memo.push(val);
|
||||||
return memo;
|
return memo;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (require.main === module) {
|
if (require.main === module) {
|
||||||
|
program
|
||||||
|
.version(packageJson.version)
|
||||||
|
.arguments('<targetUrl> [dest]')
|
||||||
|
.action((targetUrl, appDir) => {
|
||||||
|
program.targetUrl = targetUrl;
|
||||||
|
program.out = appDir;
|
||||||
|
})
|
||||||
|
.option('-n, --name <value>', 'app name')
|
||||||
|
.option('-p, --platform <value>', '\'osx\', \'linux\' or \'windows\'')
|
||||||
|
.option('-a, --arch <value>', '\'ia32\' or \'x64\'')
|
||||||
|
.option('-e, --electron-version <value>', 'electron version to package, without the \'v\', see https://github.com/atom/electron/releases')
|
||||||
|
.option('--no-overwrite', 'do not override output directory if it already exists, defaults to false')
|
||||||
|
.option('-c, --conceal', 'packages the source code within your app into an archive, defaults to false, see http://electron.atom.io/docs/v0.36.0/tutorial/application-packaging/')
|
||||||
|
.option('--counter', 'if the target app should use a persistant counter badge in the dock (OSX only), defaults to false')
|
||||||
|
.option('-i, --icon <value>', 'the icon file to use as the icon for the app (should be a .icns file on OSX, .png for Windows and Linux)')
|
||||||
|
.option('--width <value>', 'set window default width, defaults to 1280px', parseInt)
|
||||||
|
.option('--height <value>', 'set window default height, defaults to 800px', parseInt)
|
||||||
|
.option('--min-width <value>', 'set window minimum width, defaults to 0px', parseInt)
|
||||||
|
.option('--min-height <value>', 'set window minimum height, defaults to 0px', parseInt)
|
||||||
|
.option('--max-width <value>', 'set window maximum width, default is no limit', parseInt)
|
||||||
|
.option('--max-height <value>', 'set window maximum height, default is no limit', parseInt)
|
||||||
|
.option('-m, --show-menu-bar', 'set menu bar visible, defaults to false')
|
||||||
|
.option('-f, --fast-quit', 'quit app after window close (OSX only), defaults to false')
|
||||||
|
.option('-u, --user-agent <value>', 'set the user agent string for the app')
|
||||||
|
.option('--honest', 'prevent the nativefied app from changing the user agent string to masquerade as a regular chrome browser')
|
||||||
|
.option('--ignore-certificate', 'ignore certificate related errors')
|
||||||
|
.option('--insecure', 'enable loading of insecure content, defaults to false')
|
||||||
|
.option('--flash', 'if flash should be enabled')
|
||||||
|
.option('--flash-path <value>', 'path to Chrome flash plugin, find it in `Chrome://plugins`')
|
||||||
|
.option('--inject <value>', 'path to a CSS/JS file to be injected', collect, [])
|
||||||
|
.option('--full-screen', 'if the app should always be started in full screen')
|
||||||
|
.option('--maximize', 'if the app should always be started maximized')
|
||||||
|
.option('--hide-window-frame', 'disable window frame and controls')
|
||||||
|
.option('--verbose', 'if verbose logs should be displayed')
|
||||||
|
.option('--disable-context-menu', 'disable the context menu')
|
||||||
|
.option('--disable-dev-tools', 'disable developer tools')
|
||||||
|
.option('--zoom <value>', 'default zoom factor to use when the app is opened, defaults to 1.0', parseFloat)
|
||||||
|
.option('--internal-urls <value>', 'regular expression of URLs to consider "internal"; all other URLs will be opened in an external browser. (default: URLs on same second-level domain as app)')
|
||||||
|
.option('--crash-reporter <value>', 'remote server URL to send crash reports')
|
||||||
|
.option('--single-instance', 'allow only a single instance of the application')
|
||||||
|
.parse(process.argv);
|
||||||
|
|
||||||
program
|
if (!process.argv.slice(2).length) {
|
||||||
.version(packageJson.version)
|
program.help();
|
||||||
.arguments('<targetUrl> [dest]')
|
}
|
||||||
.action(function(targetUrl, appDir) {
|
|
||||||
program.targetUrl = targetUrl;
|
|
||||||
program.out = appDir;
|
|
||||||
})
|
|
||||||
.option('-n, --name <value>', 'app name')
|
|
||||||
.option('-p, --platform <value>', '\'osx\', \'linux\' or \'windows\'')
|
|
||||||
.option('-a, --arch <value>', '\'ia32\' or \'x64\'')
|
|
||||||
.option('-e, --electron-version <value>', 'electron version to package, without the \'v\', see https://github.com/atom/electron/releases')
|
|
||||||
.option('--no-overwrite', 'do not override output directory if it already exists, defaults to false')
|
|
||||||
.option('-c, --conceal', 'packages the source code within your app into an archive, defaults to false, see http://electron.atom.io/docs/v0.36.0/tutorial/application-packaging/')
|
|
||||||
.option('--counter', 'if the target app should use a persistant counter badge in the dock (OSX only), defaults to false')
|
|
||||||
.option('-i, --icon <value>', 'the icon file to use as the icon for the app (should be a .icns file on OSX, .png for Windows and Linux)')
|
|
||||||
.option('--width <value>', 'set window default width, defaults to 1280px', parseInt)
|
|
||||||
.option('--height <value>', 'set window default height, defaults to 800px', parseInt)
|
|
||||||
.option('--min-width <value>', 'set window minimum width, defaults to 0px', parseInt)
|
|
||||||
.option('--min-height <value>', 'set window minimum height, defaults to 0px', parseInt)
|
|
||||||
.option('--max-width <value>', 'set window maximum width, default is no limit', parseInt)
|
|
||||||
.option('--max-height <value>', 'set window maximum height, default is no limit', parseInt)
|
|
||||||
.option('-m, --show-menu-bar', 'set menu bar visible, defaults to false')
|
|
||||||
.option('-f, --fast-quit', 'quit app after window close (OSX only), defaults to false')
|
|
||||||
.option('-u, --user-agent <value>', 'set the user agent string for the app')
|
|
||||||
.option('--honest', 'prevent the nativefied app from changing the user agent string to masquerade as a regular chrome browser')
|
|
||||||
.option('--ignore-certificate', 'ignore certificate related errors')
|
|
||||||
.option('--insecure', 'enable loading of insecure content, defaults to false')
|
|
||||||
.option('--flash', 'if flash should be enabled')
|
|
||||||
.option('--flash-path <value>', 'path to Chrome flash plugin, find it in `Chrome://plugins`')
|
|
||||||
.option('--inject <value>', 'path to a CSS/JS file to be injected', collect, [])
|
|
||||||
.option('--full-screen', 'if the app should always be started in full screen')
|
|
||||||
.option('--maximize', 'if the app should always be started maximized')
|
|
||||||
.option('--hide-window-frame', 'disable window frame and controls')
|
|
||||||
.option('--verbose', 'if verbose logs should be displayed')
|
|
||||||
.option('--disable-context-menu', 'disable the context menu')
|
|
||||||
.option('--disable-dev-tools', 'disable developer tools')
|
|
||||||
.option('--zoom <value>', 'default zoom factor to use when the app is opened, defaults to 1.0', parseFloat)
|
|
||||||
.option('--internal-urls <value>', 'regular expression of URLs to consider "internal"; all other URLs will be opened in an external browser. (default: URLs on same second-level domain as app)')
|
|
||||||
.option('--crash-reporter <value>', 'remote server URL to send crash reports')
|
|
||||||
.option('--single-instance', 'allow only a single instance of the application')
|
|
||||||
.parse(process.argv);
|
|
||||||
|
|
||||||
if (!process.argv.slice(2).length) {
|
nativefier(program, (error, appPath) => {
|
||||||
program.help();
|
if (error) {
|
||||||
|
console.error(error);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
nativefier(program, (error, appPath) => {
|
if (!appPath) {
|
||||||
if (error) {
|
// app exists and --overwrite is not passed
|
||||||
console.error(error);
|
return;
|
||||||
return;
|
}
|
||||||
}
|
console.log(`App built to ${appPath}`);
|
||||||
|
});
|
||||||
if (!appPath) {
|
|
||||||
// app exists and --overwrite is not passed
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log(`App built to ${appPath}`);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import shell from 'shelljs';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import tmp from 'tmp';
|
import tmp from 'tmp';
|
||||||
import helpers from './helpers';
|
import helpers from './helpers';
|
||||||
|
|
||||||
const isOSX = helpers.isOSX;
|
const isOSX = helpers.isOSX;
|
||||||
tmp.setGracefulCleanup();
|
tmp.setGracefulCleanup();
|
||||||
|
|
||||||
@ -20,27 +21,27 @@ const PNG_TO_ICNS_BIN_PATH = path.join(__dirname, '../..', 'bin/convertToIcns');
|
|||||||
* @param {pngToIcnsCallback} callback
|
* @param {pngToIcnsCallback} callback
|
||||||
*/
|
*/
|
||||||
function convertToIcns(pngSrc, icnsDest, callback) {
|
function convertToIcns(pngSrc, icnsDest, callback) {
|
||||||
if (!isOSX()) {
|
if (!isOSX()) {
|
||||||
callback('OSX is required to convert .png to .icns icon', pngSrc);
|
callback('OSX is required to convert .png to .icns icon', pngSrc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
shell.exec(`${PNG_TO_ICNS_BIN_PATH} ${pngSrc} ${icnsDest}`, { silent: true }, (exitCode, stdOut, stdError) => {
|
||||||
|
if (stdOut.includes('icon.iconset:error') || exitCode) {
|
||||||
|
if (exitCode) {
|
||||||
|
callback({
|
||||||
|
stdOut,
|
||||||
|
stdError,
|
||||||
|
}, pngSrc);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(stdOut, pngSrc);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
shell.exec(`${PNG_TO_ICNS_BIN_PATH} ${pngSrc} ${icnsDest}`, {silent: true}, (exitCode, stdOut, stdError) => {
|
callback(null, icnsDest);
|
||||||
if (stdOut.includes('icon.iconset:error') || exitCode) {
|
});
|
||||||
if (exitCode) {
|
|
||||||
callback({
|
|
||||||
stdOut: stdOut,
|
|
||||||
stdError: stdError
|
|
||||||
}, pngSrc);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(stdOut, pngSrc);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(null, icnsDest);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -49,9 +50,9 @@ function convertToIcns(pngSrc, icnsDest, callback) {
|
|||||||
* @param {pngToIcnsCallback} callback
|
* @param {pngToIcnsCallback} callback
|
||||||
*/
|
*/
|
||||||
function convertToIcnsTmp(pngSrc, callback) {
|
function convertToIcnsTmp(pngSrc, callback) {
|
||||||
const tempIconDirObj = tmp.dirSync({unsafeCleanup: true});
|
const tempIconDirObj = tmp.dirSync({ unsafeCleanup: true });
|
||||||
const tempIconDirPath = tempIconDirObj.name;
|
const tempIconDirPath = tempIconDirObj.name;
|
||||||
convertToIcns(pngSrc, `${tempIconDirPath}/icon.icns`, callback);
|
convertToIcns(pngSrc, `${tempIconDirPath}/icon.icns`, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default convertToIcnsTmp;
|
export default convertToIcnsTmp;
|
||||||
|
@ -1,68 +1,70 @@
|
|||||||
import ProgressBar from 'progress';
|
import ProgressBar from 'progress';
|
||||||
|
|
||||||
class DishonestProgress {
|
class DishonestProgress {
|
||||||
constructor(total) {
|
constructor(total) {
|
||||||
this.tickParts = total * 10;
|
this.tickParts = total * 10;
|
||||||
|
|
||||||
this.bar = new ProgressBar(' :task [:bar] :percent', {
|
this.bar = new ProgressBar(' :task [:bar] :percent', {
|
||||||
complete: '=',
|
complete: '=',
|
||||||
incomplete: ' ',
|
incomplete: ' ',
|
||||||
total: total * this.tickParts,
|
total: total * this.tickParts,
|
||||||
width: 50,
|
width: 50,
|
||||||
clear: true
|
clear: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.tickingPrevious = {
|
this.tickingPrevious = {
|
||||||
message: '',
|
message: '',
|
||||||
remainder: 0,
|
remainder: 0,
|
||||||
interval: null
|
interval: null,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
tick(message) {
|
||||||
|
const {
|
||||||
|
remainder: prevRemainder,
|
||||||
|
message: prevMessage,
|
||||||
|
interval: prevInterval,
|
||||||
|
} = this.tickingPrevious;
|
||||||
|
|
||||||
|
if (prevRemainder) {
|
||||||
|
this.bar.tick(prevRemainder, {
|
||||||
|
task: prevMessage,
|
||||||
|
});
|
||||||
|
clearInterval(prevInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
tick(message) {
|
const realRemainder = this.bar.total - this.bar.curr;
|
||||||
|
if (realRemainder === this.tickParts) {
|
||||||
const {remainder: prevRemainder, message: prevMessage, interval: prevInterval} = this.tickingPrevious;
|
this.bar.tick(this.tickParts, {
|
||||||
|
task: message,
|
||||||
if (prevRemainder) {
|
});
|
||||||
this.bar.tick(prevRemainder, {
|
return;
|
||||||
task: prevMessage
|
|
||||||
});
|
|
||||||
clearInterval(prevInterval);
|
|
||||||
}
|
|
||||||
|
|
||||||
const realRemainder = this.bar.total - this.bar.curr;
|
|
||||||
if (realRemainder === this.tickParts) {
|
|
||||||
this.bar.tick(this.tickParts, {
|
|
||||||
task: message
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.bar.tick({
|
|
||||||
task: message
|
|
||||||
});
|
|
||||||
|
|
||||||
this.tickingPrevious = {
|
|
||||||
message: message,
|
|
||||||
remainder: this.tickParts,
|
|
||||||
interval: null
|
|
||||||
};
|
|
||||||
|
|
||||||
this.tickingPrevious.remainder -= 1;
|
|
||||||
|
|
||||||
this.tickingPrevious.interval = setInterval(() => {
|
|
||||||
if (this.tickingPrevious.remainder === 1) {
|
|
||||||
clearInterval(this.tickingPrevious.interval);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.bar.tick({
|
|
||||||
task: message
|
|
||||||
});
|
|
||||||
this.tickingPrevious.remainder -= 1;
|
|
||||||
}, 200);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.bar.tick({
|
||||||
|
task: message,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.tickingPrevious = {
|
||||||
|
message,
|
||||||
|
remainder: this.tickParts,
|
||||||
|
interval: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.tickingPrevious.remainder -= 1;
|
||||||
|
|
||||||
|
this.tickingPrevious.interval = setInterval(() => {
|
||||||
|
if (this.tickingPrevious.remainder === 1) {
|
||||||
|
clearInterval(this.tickingPrevious.interval);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.bar.tick({
|
||||||
|
task: message,
|
||||||
|
});
|
||||||
|
this.tickingPrevious.remainder -= 1;
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DishonestProgress;
|
export default DishonestProgress;
|
||||||
|
@ -4,100 +4,100 @@ import hasBinary from 'hasbin';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
function isOSX() {
|
function isOSX() {
|
||||||
return os.platform() === 'darwin';
|
return os.platform() === 'darwin';
|
||||||
}
|
}
|
||||||
|
|
||||||
function isWindows() {
|
function isWindows() {
|
||||||
return os.platform() === 'win32';
|
return os.platform() === 'win32';
|
||||||
}
|
}
|
||||||
|
|
||||||
function downloadFile(fileUrl) {
|
function downloadFile(fileUrl) {
|
||||||
return axios.get(
|
return axios.get(
|
||||||
fileUrl, {
|
fileUrl, {
|
||||||
responseType: 'arraybuffer'
|
responseType: 'arraybuffer',
|
||||||
})
|
})
|
||||||
.then(function(response) {
|
.then((response) => {
|
||||||
if (!response.data) {
|
if (!response.data) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
data: response.data,
|
data: response.data,
|
||||||
ext: path.extname(fileUrl)
|
ext: path.extname(fileUrl),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function allowedIconFormats(platform) {
|
function allowedIconFormats(platform) {
|
||||||
const hasIdentify = hasBinary.sync('identify');
|
const hasIdentify = hasBinary.sync('identify');
|
||||||
const hasConvert = hasBinary.sync('convert');
|
const hasConvert = hasBinary.sync('convert');
|
||||||
const hasIconUtil = hasBinary.sync('iconutil');
|
const hasIconUtil = hasBinary.sync('iconutil');
|
||||||
|
|
||||||
const pngToIcns = hasConvert && hasIconUtil;
|
const pngToIcns = hasConvert && hasIconUtil;
|
||||||
const pngToIco = hasConvert;
|
const pngToIco = hasConvert;
|
||||||
const icoToIcns = pngToIcns && hasIdentify;
|
const icoToIcns = pngToIcns && hasIdentify;
|
||||||
const icoToPng = hasConvert;
|
const icoToPng = hasConvert;
|
||||||
|
|
||||||
// todo scripts for the following
|
// todo scripts for the following
|
||||||
const icnsToPng = false;
|
const icnsToPng = false;
|
||||||
const icnsToIco = false;
|
const icnsToIco = false;
|
||||||
|
|
||||||
const formats = [];
|
const formats = [];
|
||||||
|
|
||||||
// todo shell scripting is not supported on windows, temporary override
|
|
||||||
if (isWindows()) {
|
|
||||||
switch (platform) {
|
|
||||||
case 'darwin':
|
|
||||||
formats.push('.icns');
|
|
||||||
break;
|
|
||||||
case 'linux':
|
|
||||||
formats.push('.png');
|
|
||||||
break;
|
|
||||||
case 'win32':
|
|
||||||
formats.push('.ico');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw `function allowedIconFormats error: Unknown platform ${platform}`;
|
|
||||||
}
|
|
||||||
return formats;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// todo shell scripting is not supported on windows, temporary override
|
||||||
|
if (isWindows()) {
|
||||||
switch (platform) {
|
switch (platform) {
|
||||||
case 'darwin':
|
case 'darwin':
|
||||||
formats.push('.icns');
|
formats.push('.icns');
|
||||||
if (pngToIcns) {
|
break;
|
||||||
formats.push('.png');
|
case 'linux':
|
||||||
}
|
formats.push('.png');
|
||||||
if (icoToIcns) {
|
break;
|
||||||
formats.push('.ico');
|
case 'win32':
|
||||||
}
|
formats.push('.ico');
|
||||||
break;
|
break;
|
||||||
case 'linux':
|
default:
|
||||||
formats.push('.png');
|
throw new Error(`function allowedIconFormats error: Unknown platform ${platform}`);
|
||||||
if (icoToPng) {
|
|
||||||
formats.push('.ico');
|
|
||||||
}
|
|
||||||
if (icnsToPng) {
|
|
||||||
formats.push('.icns');
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'win32':
|
|
||||||
formats.push('.ico');
|
|
||||||
if (pngToIco) {
|
|
||||||
formats.push('.png');
|
|
||||||
}
|
|
||||||
if (icnsToIco) {
|
|
||||||
formats.push('.icns');
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw `function allowedIconFormats error: Unknown platform ${platform}`;
|
|
||||||
}
|
}
|
||||||
return formats;
|
return formats;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (platform) {
|
||||||
|
case 'darwin':
|
||||||
|
formats.push('.icns');
|
||||||
|
if (pngToIcns) {
|
||||||
|
formats.push('.png');
|
||||||
|
}
|
||||||
|
if (icoToIcns) {
|
||||||
|
formats.push('.ico');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'linux':
|
||||||
|
formats.push('.png');
|
||||||
|
if (icoToPng) {
|
||||||
|
formats.push('.ico');
|
||||||
|
}
|
||||||
|
if (icnsToPng) {
|
||||||
|
formats.push('.icns');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'win32':
|
||||||
|
formats.push('.ico');
|
||||||
|
if (pngToIco) {
|
||||||
|
formats.push('.png');
|
||||||
|
}
|
||||||
|
if (icnsToIco) {
|
||||||
|
formats.push('.icns');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(`function allowedIconFormats error: Unknown platform ${platform}`);
|
||||||
|
}
|
||||||
|
return formats;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
isOSX,
|
isOSX,
|
||||||
isWindows,
|
isWindows,
|
||||||
downloadFile,
|
downloadFile,
|
||||||
allowedIconFormats
|
allowedIconFormats,
|
||||||
};
|
};
|
||||||
|
@ -2,15 +2,16 @@ import shell from 'shelljs';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import tmp from 'tmp';
|
import tmp from 'tmp';
|
||||||
import helpers from './helpers';
|
import helpers from './helpers';
|
||||||
const {isWindows, isOSX} = helpers;
|
|
||||||
|
const { isWindows, isOSX } = helpers;
|
||||||
|
|
||||||
tmp.setGracefulCleanup();
|
tmp.setGracefulCleanup();
|
||||||
|
|
||||||
const SCRIPT_PATHS = {
|
const SCRIPT_PATHS = {
|
||||||
singleIco: path.join(__dirname, '../..', 'bin/singleIco'),
|
singleIco: path.join(__dirname, '../..', 'bin/singleIco'),
|
||||||
convertToPng: path.join(__dirname, '../..', 'bin/convertToPng'),
|
convertToPng: path.join(__dirname, '../..', 'bin/convertToPng'),
|
||||||
convertToIco: path.join(__dirname, '../..', 'bin/convertToIco'),
|
convertToIco: path.join(__dirname, '../..', 'bin/convertToIco'),
|
||||||
convertToIcns: path.join(__dirname, '../..', 'bin/convertToIcns')
|
convertToIcns: path.join(__dirname, '../..', 'bin/convertToIcns'),
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -20,29 +21,29 @@ const SCRIPT_PATHS = {
|
|||||||
* @param {string} dest has to be a .ico path
|
* @param {string} dest has to be a .ico path
|
||||||
*/
|
*/
|
||||||
function iconShellHelper(shellScriptPath, icoSrc, dest) {
|
function iconShellHelper(shellScriptPath, icoSrc, dest) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (isWindows()) {
|
if (isWindows()) {
|
||||||
reject('OSX or Linux is required');
|
reject('OSX or Linux is required');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
shell.exec(`${shellScriptPath} ${icoSrc} ${dest}`, {silent: true}, (exitCode, stdOut, stdError) => {
|
shell.exec(`${shellScriptPath} ${icoSrc} ${dest}`, { silent: true }, (exitCode, stdOut, stdError) => {
|
||||||
if (exitCode) {
|
if (exitCode) {
|
||||||
reject({
|
reject({
|
||||||
stdOut: stdOut,
|
stdOut,
|
||||||
stdError: stdError
|
stdError,
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(dest);
|
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(dest);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTmpDirPath() {
|
function getTmpDirPath() {
|
||||||
const tempIconDirObj = tmp.dirSync({unsafeCleanup: true});
|
const tempIconDirObj = tmp.dirSync({ unsafeCleanup: true });
|
||||||
return tempIconDirObj.name;
|
return tempIconDirObj.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,27 +53,27 @@ function getTmpDirPath() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function singleIco(icoSrc) {
|
function singleIco(icoSrc) {
|
||||||
return iconShellHelper(SCRIPT_PATHS.singleIco, icoSrc, `${getTmpDirPath()}/icon.ico`);
|
return iconShellHelper(SCRIPT_PATHS.singleIco, icoSrc, `${getTmpDirPath()}/icon.ico`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertToPng(icoSrc) {
|
function convertToPng(icoSrc) {
|
||||||
return iconShellHelper(SCRIPT_PATHS.convertToPng, icoSrc, `${getTmpDirPath()}/icon.png`);
|
return iconShellHelper(SCRIPT_PATHS.convertToPng, icoSrc, `${getTmpDirPath()}/icon.png`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertToIco(icoSrc) {
|
function convertToIco(icoSrc) {
|
||||||
return iconShellHelper(SCRIPT_PATHS.convertToIco, icoSrc, `${getTmpDirPath()}/icon.ico`);
|
return iconShellHelper(SCRIPT_PATHS.convertToIco, icoSrc, `${getTmpDirPath()}/icon.ico`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertToIcns(icoSrc) {
|
function convertToIcns(icoSrc) {
|
||||||
if (!isOSX()) {
|
if (!isOSX()) {
|
||||||
return new Promise((resolve, reject) => reject('OSX is required to convert to a .icns icon'));
|
return new Promise((resolve, reject) => reject('OSX is required to convert to a .icns icon'));
|
||||||
}
|
}
|
||||||
return iconShellHelper(SCRIPT_PATHS.convertToIcns, icoSrc, `${getTmpDirPath()}/icon.icns`);
|
return iconShellHelper(SCRIPT_PATHS.convertToIcns, icoSrc, `${getTmpDirPath()}/icon.icns`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
singleIco,
|
singleIco,
|
||||||
convertToPng,
|
convertToPng,
|
||||||
convertToIco,
|
convertToIco,
|
||||||
convertToIcns
|
convertToIcns,
|
||||||
};
|
};
|
||||||
|
@ -1,27 +1,29 @@
|
|||||||
|
// TODO: remove this file and use quiet mode of new version of electron packager
|
||||||
class PackagerConsole {
|
class PackagerConsole {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.logs = [];
|
this.logs = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
_log(...messages) {
|
_log(...messages) {
|
||||||
this.logs.push(...messages);
|
this.logs.push(...messages);
|
||||||
}
|
}
|
||||||
|
|
||||||
override() {
|
override() {
|
||||||
this.consoleError = console.error;
|
this.consoleError = console.error;
|
||||||
|
|
||||||
// need to bind because somehow when _log() is called this refers to console
|
// need to bind because somehow when _log() is called this refers to console
|
||||||
console.error = this._log.bind(this);
|
// eslint-disable-next-line no-underscore-dangle
|
||||||
}
|
console.error = this._log.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
restore() {
|
restore() {
|
||||||
console.error = this.consoleError;
|
console.error = this.consoleError;
|
||||||
}
|
}
|
||||||
|
|
||||||
playback() {
|
playback() {
|
||||||
console.log(this.logs.join(' '));
|
console.log(this.logs.join(' '));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PackagerConsole;
|
export default PackagerConsole;
|
||||||
|
@ -5,117 +5,100 @@ import tmp from 'tmp';
|
|||||||
import gitCloud from 'gitcloud';
|
import gitCloud from 'gitcloud';
|
||||||
import helpers from './../helpers/helpers';
|
import helpers from './../helpers/helpers';
|
||||||
|
|
||||||
const {downloadFile, allowedIconFormats} = helpers;
|
const { downloadFile, allowedIconFormats } = helpers;
|
||||||
tmp.setGracefulCleanup();
|
tmp.setGracefulCleanup();
|
||||||
|
|
||||||
const GITCLOUD_SPACE_DELIMITER = '-';
|
const GITCLOUD_SPACE_DELIMITER = '-';
|
||||||
|
|
||||||
function inferIconFromStore(targetUrl, platform) {
|
|
||||||
const allowedFormats = allowedIconFormats(platform);
|
|
||||||
|
|
||||||
return gitCloud('http://jiahaog.com/nativefier-icons/')
|
|
||||||
.then(fileIndex => {
|
|
||||||
const iconWithScores = mapIconWithMatchScore(fileIndex, targetUrl);
|
|
||||||
const maxScore = getMaxMatchScore(iconWithScores);
|
|
||||||
|
|
||||||
if (maxScore === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const matchingIcons = getMatchingIcons(iconWithScores, maxScore);
|
|
||||||
|
|
||||||
let matchingUrl;
|
|
||||||
for (let format of allowedFormats) {
|
|
||||||
for (let icon of matchingIcons) {
|
|
||||||
if (icon.ext !== format) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
matchingUrl = icon.url;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!matchingUrl) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return downloadFile(matchingUrl);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapIconWithMatchScore(fileIndex, targetUrl) {
|
|
||||||
const normalisedTargetUrl = targetUrl.toLowerCase();
|
|
||||||
return fileIndex
|
|
||||||
.map(item => {
|
|
||||||
const itemWords = item.name.split(GITCLOUD_SPACE_DELIMITER);
|
|
||||||
const score = itemWords.reduce((currentScore, word) => {
|
|
||||||
if (normalisedTargetUrl.includes(word)) {
|
|
||||||
return currentScore + 1;
|
|
||||||
}
|
|
||||||
return currentScore;
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
return Object.assign({},
|
|
||||||
item,
|
|
||||||
{score}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMaxMatchScore(iconWithScores) {
|
function getMaxMatchScore(iconWithScores) {
|
||||||
return iconWithScores.reduce((maxScore, currentIcon) => {
|
return iconWithScores.reduce((maxScore, currentIcon) => {
|
||||||
const currentScore = currentIcon.score;
|
const currentScore = currentIcon.score;
|
||||||
if (currentScore > maxScore) {
|
if (currentScore > maxScore) {
|
||||||
return currentScore;
|
return currentScore;
|
||||||
}
|
}
|
||||||
return maxScore;
|
return maxScore;
|
||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* also maps ext to icon object
|
* also maps ext to icon object
|
||||||
*/
|
*/
|
||||||
function getMatchingIcons(iconWithScores, maxScore) {
|
function getMatchingIcons(iconsWithScores, maxScore) {
|
||||||
return iconWithScores
|
return iconsWithScores
|
||||||
.filter(item => {
|
.filter(item => item.score === maxScore)
|
||||||
return item.score === maxScore;
|
.map(item => Object.assign({}, item, { ext: path.extname(item.url) }));
|
||||||
})
|
|
||||||
.map(item => {
|
|
||||||
return Object.assign(
|
|
||||||
{},
|
|
||||||
item,
|
|
||||||
{ext: path.extname(item.url)}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeFilePromise(outPath, data) {
|
function mapIconWithMatchScore(fileIndex, targetUrl) {
|
||||||
return new Promise((resolve, reject) => {
|
const normalisedTargetUrl = targetUrl.toLowerCase();
|
||||||
fs.writeFile(outPath, data, error => {
|
return fileIndex
|
||||||
if (error) {
|
.map((item) => {
|
||||||
reject(error);
|
const itemWords = item.name.split(GITCLOUD_SPACE_DELIMITER);
|
||||||
return;
|
const score = itemWords.reduce((currentScore, word) => {
|
||||||
}
|
if (normalisedTargetUrl.includes(word)) {
|
||||||
resolve(outPath);
|
return currentScore + 1;
|
||||||
});
|
}
|
||||||
|
return currentScore;
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
return Object.assign({}, item, { score });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function inferFromPage(targetUrl, platform, outDir) {
|
function inferIconFromStore(targetUrl, platform) {
|
||||||
let preferredExt = '.png';
|
const allowedFormats = new Set(allowedIconFormats(platform));
|
||||||
if (platform === 'win32') {
|
|
||||||
preferredExt = '.ico';
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo might want to pass list of preferences instead
|
return gitCloud('http://jiahaog.com/nativefier-icons/')
|
||||||
return pageIcon(targetUrl, {ext: preferredExt})
|
.then((fileIndex) => {
|
||||||
.then(icon => {
|
const iconWithScores = mapIconWithMatchScore(fileIndex, targetUrl);
|
||||||
if (!icon) {
|
const maxScore = getMaxMatchScore(iconWithScores);
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const outfilePath = path.join(outDir, `/icon${icon.ext}`);
|
if (maxScore === 0) {
|
||||||
return writeFilePromise(outfilePath, icon.data);
|
return null;
|
||||||
});
|
}
|
||||||
|
|
||||||
|
const iconsMatchingScore = getMatchingIcons(iconWithScores, maxScore);
|
||||||
|
const iconsMatchingExt = iconsMatchingScore.filter(icon => allowedFormats.has(icon.ext));
|
||||||
|
const matchingIcon = iconsMatchingExt[0];
|
||||||
|
const iconUrl = matchingIcon && matchingIcon.url;
|
||||||
|
|
||||||
|
if (!iconUrl) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return downloadFile(iconUrl);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function writeFilePromise(outPath, data) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
fs.writeFile(outPath, data, (error) => {
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve(outPath);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function inferFromPage(targetUrl, platform, outDir) {
|
||||||
|
let preferredExt = '.png';
|
||||||
|
if (platform === 'win32') {
|
||||||
|
preferredExt = '.ico';
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo might want to pass list of preferences instead
|
||||||
|
return pageIcon(targetUrl, { ext: preferredExt })
|
||||||
|
.then((icon) => {
|
||||||
|
if (!icon) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const outfilePath = path.join(outDir, `/icon${icon.ext}`);
|
||||||
|
return writeFilePromise(outfilePath, icon.data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {string} targetUrl
|
* @param {string} targetUrl
|
||||||
@ -123,16 +106,15 @@ function inferFromPage(targetUrl, platform, outDir) {
|
|||||||
* @param {string} outDir
|
* @param {string} outDir
|
||||||
*/
|
*/
|
||||||
function inferIconFromUrlToPath(targetUrl, platform, outDir) {
|
function inferIconFromUrlToPath(targetUrl, platform, outDir) {
|
||||||
|
return inferIconFromStore(targetUrl, platform)
|
||||||
|
.then((icon) => {
|
||||||
|
if (!icon) {
|
||||||
|
return inferFromPage(targetUrl, platform, outDir);
|
||||||
|
}
|
||||||
|
|
||||||
return inferIconFromStore(targetUrl, platform)
|
const outfilePath = path.join(outDir, `/icon${icon.ext}`);
|
||||||
.then(icon => {
|
return writeFilePromise(outfilePath, icon.data);
|
||||||
if (!icon) {
|
});
|
||||||
return inferFromPage(targetUrl, platform, outDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
const outfilePath = path.join(outDir, `/icon${icon.ext}`);
|
|
||||||
return writeFilePromise(outfilePath, icon.data);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -140,9 +122,9 @@ function inferIconFromUrlToPath(targetUrl, platform, outDir) {
|
|||||||
* @param {string} platform
|
* @param {string} platform
|
||||||
*/
|
*/
|
||||||
function inferIcon(targetUrl, platform) {
|
function inferIcon(targetUrl, platform) {
|
||||||
const tmpObj = tmp.dirSync({unsafeCleanup: true});
|
const tmpObj = tmp.dirSync({ unsafeCleanup: true });
|
||||||
const tmpPath = tmpObj.name;
|
const tmpPath = tmpObj.name;
|
||||||
return inferIconFromUrlToPath(targetUrl, platform, tmpPath);
|
return inferIconFromUrlToPath(targetUrl, platform, tmpPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default inferIcon;
|
export default inferIcon;
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
import os from 'os';
|
import os from 'os';
|
||||||
|
|
||||||
function inferPlatform() {
|
function inferPlatform() {
|
||||||
const platform = os.platform();
|
const platform = os.platform();
|
||||||
if (platform === 'darwin' || platform === 'win32' || platform === 'linux') {
|
if (platform === 'darwin' || platform === 'win32' || platform === 'linux') {
|
||||||
return platform;
|
return platform;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw `Untested platform ${platform} detected`;
|
throw new Error(`Untested platform ${platform} detected`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function inferArch() {
|
function inferArch() {
|
||||||
const arch = os.arch();
|
const arch = os.arch();
|
||||||
if (arch !== 'ia32' && arch !== 'x64') {
|
if (arch !== 'ia32' && arch !== 'x64') {
|
||||||
throw `Incompatible architecture ${arch} detected`;
|
throw new Error(`Incompatible architecture ${arch} detected`);
|
||||||
}
|
}
|
||||||
return arch;
|
return arch;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inferPlatform: inferPlatform,
|
inferPlatform,
|
||||||
inferArch: inferArch
|
inferArch,
|
||||||
};
|
};
|
||||||
|
@ -4,19 +4,19 @@ import cheerio from 'cheerio';
|
|||||||
const USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.1 Safari/537.36';
|
const USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.1 Safari/537.36';
|
||||||
|
|
||||||
function inferTitle(url) {
|
function inferTitle(url) {
|
||||||
const options = {
|
const options = {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url,
|
url,
|
||||||
headers: {
|
headers: {
|
||||||
// fake a user agent because pages like http://messenger.com will throw 404 error
|
// fake a user agent because pages like http://messenger.com will throw 404 error
|
||||||
'User-Agent': USER_AGENT
|
'User-Agent': USER_AGENT,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return axios(options).then(({data}) => {
|
return axios(options).then(({ data }) => {
|
||||||
const $ = cheerio.load(data);
|
const $ = cheerio.load(data);
|
||||||
return $('title').first().text().replace(/\//g, '');
|
return $('title').first().text().replace(/\//g, '');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export default inferTitle;
|
export default inferTitle;
|
||||||
|
@ -6,50 +6,49 @@ const ELECTRON_VERSIONS_URL = 'https://atom.io/download/atom-shell/index.json';
|
|||||||
const DEFAULT_CHROME_VERSION = '56.0.2924.87';
|
const DEFAULT_CHROME_VERSION = '56.0.2924.87';
|
||||||
|
|
||||||
function getChromeVersionForElectronVersion(electronVersion, url = ELECTRON_VERSIONS_URL) {
|
function getChromeVersionForElectronVersion(electronVersion, url = ELECTRON_VERSIONS_URL) {
|
||||||
return axios.get(url, {timeout: 5000})
|
return axios.get(url, { timeout: 5000 })
|
||||||
.then(response => {
|
.then((response) => {
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
throw `Bad request: Status code ${response.status}`;
|
throw new Error(`Bad request: Status code ${response.status}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = response.data;
|
const data = response.data;
|
||||||
const electronVersionToChromeVersion = _.zipObject(data.map(d => d.version), data.map(d => d.chrome));
|
const electronVersionToChromeVersion = _.zipObject(data.map(d => d.version),
|
||||||
|
data.map(d => d.chrome));
|
||||||
|
|
||||||
if (!(electronVersion in electronVersionToChromeVersion)) {
|
if (!(electronVersion in electronVersionToChromeVersion)) {
|
||||||
throw `Electron version '${electronVersion}' not found in retrieved version list!`;
|
throw new Error(`Electron version '${electronVersion}' not found in retrieved version list!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return electronVersionToChromeVersion[electronVersion];
|
return electronVersionToChromeVersion[electronVersion];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getUserAgentString(chromeVersion, platform) {
|
export function getUserAgentString(chromeVersion, platform) {
|
||||||
let userAgent;
|
let userAgent;
|
||||||
switch (platform) {
|
switch (platform) {
|
||||||
case 'darwin':
|
case 'darwin':
|
||||||
userAgent = `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${chromeVersion} Safari/537.36`;
|
userAgent = `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${chromeVersion} Safari/537.36`;
|
||||||
break;
|
break;
|
||||||
case 'win32':
|
case 'win32':
|
||||||
userAgent = `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${chromeVersion} Safari/537.36`;
|
userAgent = `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${chromeVersion} Safari/537.36`;
|
||||||
break;
|
break;
|
||||||
case 'linux':
|
case 'linux':
|
||||||
userAgent = `Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${chromeVersion} Safari/537.36`;
|
userAgent = `Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${chromeVersion} Safari/537.36`;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw 'Error invalid platform specified to getUserAgentString()';
|
throw new Error('Error invalid platform specified to getUserAgentString()');
|
||||||
}
|
}
|
||||||
return userAgent;
|
return userAgent;
|
||||||
}
|
}
|
||||||
|
|
||||||
function inferUserAgent(electronVersion, platform, url = ELECTRON_VERSIONS_URL) {
|
function inferUserAgent(electronVersion, platform, url = ELECTRON_VERSIONS_URL) {
|
||||||
return getChromeVersionForElectronVersion(electronVersion, url)
|
return getChromeVersionForElectronVersion(electronVersion, url)
|
||||||
.then(chromeVersion => {
|
.then(chromeVersion => getUserAgentString(chromeVersion, platform))
|
||||||
return getUserAgentString(chromeVersion, platform);
|
.catch(() => {
|
||||||
})
|
log.warn(`Unable to infer chrome version for user agent, using ${DEFAULT_CHROME_VERSION}`);
|
||||||
.catch(() => {
|
return getUserAgentString(DEFAULT_CHROME_VERSION, platform);
|
||||||
log.warn(`Unable to infer chrome version for user agent, using ${DEFAULT_CHROME_VERSION}`);
|
});
|
||||||
return getUserAgentString(DEFAULT_CHROME_VERSION, platform);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default inferUserAgent;
|
export default inferUserAgent;
|
||||||
|
@ -2,25 +2,25 @@ import url from 'url';
|
|||||||
import validator from 'validator';
|
import validator from 'validator';
|
||||||
|
|
||||||
function appendProtocol(testUrl) {
|
function appendProtocol(testUrl) {
|
||||||
const parsed = url.parse(testUrl);
|
const parsed = url.parse(testUrl);
|
||||||
if (!parsed.protocol) {
|
if (!parsed.protocol) {
|
||||||
return `http://${testUrl}`;
|
return `http://${testUrl}`;
|
||||||
}
|
}
|
||||||
return testUrl;
|
return testUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeUrl(testUrl) {
|
function normalizeUrl(testUrl) {
|
||||||
const urlWithProtocol = appendProtocol(testUrl);
|
const urlWithProtocol = appendProtocol(testUrl);
|
||||||
|
|
||||||
const validatorOptions = {
|
const validatorOptions = {
|
||||||
require_protocol: true,
|
require_protocol: true,
|
||||||
require_tld: false,
|
require_tld: false,
|
||||||
allow_trailing_dot: true // mDNS addresses, https://github.com/jiahaog/nativefier/issues/308
|
allow_trailing_dot: true, // mDNS addresses, https://github.com/jiahaog/nativefier/issues/308
|
||||||
};
|
};
|
||||||
if (!validator.isURL(urlWithProtocol, validatorOptions)) {
|
if (!validator.isURL(urlWithProtocol, validatorOptions)) {
|
||||||
throw `Your Url: "${urlWithProtocol}" is invalid!`;
|
throw new Error(`Your Url: "${urlWithProtocol}" is invalid!`);
|
||||||
}
|
}
|
||||||
return urlWithProtocol;
|
return urlWithProtocol;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default normalizeUrl;
|
export default normalizeUrl;
|
||||||
|
@ -11,13 +11,31 @@ import inferUserAgent from './../infer/inferUserAgent';
|
|||||||
import normalizeUrl from './normalizeUrl';
|
import normalizeUrl from './normalizeUrl';
|
||||||
import packageJson from './../../package.json';
|
import packageJson from './../../package.json';
|
||||||
|
|
||||||
const {inferPlatform, inferArch} = inferOs;
|
const { inferPlatform, inferArch } = inferOs;
|
||||||
|
|
||||||
const PLACEHOLDER_APP_DIR = path.join(__dirname, '../../', 'app');
|
const PLACEHOLDER_APP_DIR = path.join(__dirname, '../../', 'app');
|
||||||
const ELECTRON_VERSION = '1.6.6';
|
const ELECTRON_VERSION = '1.6.6';
|
||||||
|
|
||||||
const DEFAULT_APP_NAME = 'APP';
|
const DEFAULT_APP_NAME = 'APP';
|
||||||
|
|
||||||
|
function sanitizeFilename(platform, str) {
|
||||||
|
let result = sanitizeFilenameLib(str);
|
||||||
|
|
||||||
|
// remove all non ascii or use default app name
|
||||||
|
// eslint-disable-next-line no-control-regex
|
||||||
|
result = result.replace(/[^\x00-\x7F]/g, '') || DEFAULT_APP_NAME;
|
||||||
|
|
||||||
|
// spaces will cause problems with Ubuntu when pinned to the dock
|
||||||
|
if (platform === 'linux') {
|
||||||
|
return _.kebabCase(result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sanitizeOptions(options) {
|
||||||
|
const name = sanitizeFilename(options.platform, options.name);
|
||||||
|
return Object.assign({}, options, { name });
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @callback optionsCallback
|
* @callback optionsCallback
|
||||||
* @param error
|
* @param error
|
||||||
@ -30,143 +48,128 @@ const DEFAULT_APP_NAME = 'APP';
|
|||||||
* @param {optionsCallback} callback
|
* @param {optionsCallback} callback
|
||||||
*/
|
*/
|
||||||
function optionsFactory(inpOptions, callback) {
|
function optionsFactory(inpOptions, callback) {
|
||||||
|
const options = {
|
||||||
const options = {
|
dir: PLACEHOLDER_APP_DIR,
|
||||||
dir: PLACEHOLDER_APP_DIR,
|
name: inpOptions.name,
|
||||||
name: inpOptions.name,
|
targetUrl: normalizeUrl(inpOptions.targetUrl),
|
||||||
targetUrl: normalizeUrl(inpOptions.targetUrl),
|
platform: inpOptions.platform || inferPlatform(),
|
||||||
platform: inpOptions.platform || inferPlatform(),
|
arch: inpOptions.arch || inferArch(),
|
||||||
arch: inpOptions.arch || inferArch(),
|
electronVersion: inpOptions.electronVersion || ELECTRON_VERSION,
|
||||||
electronVersion: inpOptions.electronVersion || ELECTRON_VERSION,
|
nativefierVersion: packageJson.version,
|
||||||
nativefierVersion: packageJson.version,
|
out: inpOptions.out || process.cwd(),
|
||||||
out: inpOptions.out || process.cwd(),
|
overwrite: inpOptions.overwrite,
|
||||||
overwrite: inpOptions.overwrite,
|
asar: inpOptions.conceal || false,
|
||||||
asar: inpOptions.conceal || false,
|
icon: inpOptions.icon,
|
||||||
icon: inpOptions.icon,
|
counter: inpOptions.counter || false,
|
||||||
counter: inpOptions.counter || false,
|
width: inpOptions.width || 1280,
|
||||||
width: inpOptions.width || 1280,
|
height: inpOptions.height || 800,
|
||||||
height: inpOptions.height || 800,
|
minWidth: inpOptions.minWidth,
|
||||||
minWidth: inpOptions.minWidth,
|
minHeight: inpOptions.minHeight,
|
||||||
minHeight: inpOptions.minHeight,
|
maxWidth: inpOptions.maxWidth,
|
||||||
maxWidth: inpOptions.maxWidth,
|
maxHeight: inpOptions.maxHeight,
|
||||||
maxHeight: inpOptions.maxHeight,
|
showMenuBar: inpOptions.showMenuBar || false,
|
||||||
showMenuBar: inpOptions.showMenuBar || false,
|
fastQuit: inpOptions.fastQuit || false,
|
||||||
fastQuit: inpOptions.fastQuit || false,
|
userAgent: inpOptions.userAgent,
|
||||||
userAgent: inpOptions.userAgent,
|
ignoreCertificate: inpOptions.ignoreCertificate || false,
|
||||||
ignoreCertificate: inpOptions.ignoreCertificate || false,
|
insecure: inpOptions.insecure || false,
|
||||||
insecure: inpOptions.insecure || false,
|
flashPluginDir: inpOptions.flashPath || inpOptions.flash || null,
|
||||||
flashPluginDir: inpOptions.flashPath || inpOptions.flash || null,
|
inject: inpOptions.inject || null,
|
||||||
inject: inpOptions.inject || null,
|
ignore: 'src',
|
||||||
ignore: 'src',
|
fullScreen: inpOptions.fullScreen || false,
|
||||||
fullScreen: inpOptions.fullScreen || false,
|
maximize: inpOptions.maximize || false,
|
||||||
maximize: inpOptions.maximize || false,
|
hideWindowFrame: inpOptions.hideWindowFrame,
|
||||||
hideWindowFrame: inpOptions.hideWindowFrame,
|
verbose: inpOptions.verbose,
|
||||||
verbose: inpOptions.verbose,
|
disableContextMenu: inpOptions.disableContextMenu,
|
||||||
disableContextMenu: inpOptions.disableContextMenu,
|
disableDevTools: inpOptions.disableDevTools,
|
||||||
disableDevTools: inpOptions.disableDevTools,
|
crashReporter: inpOptions.crashReporter,
|
||||||
crashReporter: inpOptions.crashReporter,
|
|
||||||
// workaround for electron-packager#375
|
// workaround for electron-packager#375
|
||||||
tmpdir: false,
|
tmpdir: false,
|
||||||
zoom: inpOptions.zoom || 1.0,
|
zoom: inpOptions.zoom || 1.0,
|
||||||
internalUrls: inpOptions.internalUrls || null,
|
internalUrls: inpOptions.internalUrls || null,
|
||||||
singleInstance: inpOptions.singleInstance || false
|
singleInstance: inpOptions.singleInstance || false,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (options.verbose) {
|
if (options.verbose) {
|
||||||
log.setLevel('trace');
|
log.setLevel('trace');
|
||||||
} else {
|
} else {
|
||||||
log.setLevel('error');
|
log.setLevel('error');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.flashPluginDir) {
|
||||||
|
options.insecure = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inpOptions.honest) {
|
||||||
|
options.userAgent = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.platform.toLowerCase() === 'windows') {
|
||||||
|
options.platform = 'win32';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.platform.toLowerCase() === 'osx' || options.platform.toLowerCase() === 'mac') {
|
||||||
|
options.platform = 'darwin';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.width > options.maxWidth) {
|
||||||
|
options.width = options.maxWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.height > options.maxHeight) {
|
||||||
|
options.height = options.maxHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
async.waterfall([
|
||||||
|
(callback) => {
|
||||||
|
if (options.userAgent) {
|
||||||
|
callback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
inferUserAgent(options.electronVersion, options.platform)
|
||||||
|
.then((userAgent) => {
|
||||||
|
options.userAgent = userAgent;
|
||||||
|
callback();
|
||||||
|
})
|
||||||
|
.catch(callback);
|
||||||
|
},
|
||||||
|
(callback) => {
|
||||||
|
if (options.icon) {
|
||||||
|
callback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
inferIcon(options.targetUrl, options.platform)
|
||||||
|
.then((pngPath) => {
|
||||||
|
options.icon = pngPath;
|
||||||
|
callback();
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
log.warn('Cannot automatically retrieve the app icon:', error);
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
(callback) => {
|
||||||
|
// length also checks if its the commanderJS function or a string
|
||||||
|
if (options.name && options.name.length > 0) {
|
||||||
|
callback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
options.name = DEFAULT_APP_NAME;
|
||||||
|
|
||||||
|
inferTitle(options.targetUrl).then((pageTitle) => {
|
||||||
|
options.name = pageTitle;
|
||||||
|
}).catch((error) => {
|
||||||
|
log.warn(`Unable to automatically determine app name, falling back to '${DEFAULT_APP_NAME}'. Reason: ${error}`);
|
||||||
|
}).then(() => {
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
], (error) => {
|
||||||
|
if (error) {
|
||||||
|
callback(error);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
callback(null, sanitizeOptions(options));
|
||||||
if (options.flashPluginDir) {
|
});
|
||||||
options.insecure = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inpOptions.honest) {
|
|
||||||
options.userAgent = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.platform.toLowerCase() === 'windows') {
|
|
||||||
options.platform = 'win32';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.platform.toLowerCase() === 'osx' || options.platform.toLowerCase() === 'mac') {
|
|
||||||
options.platform = 'darwin';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.width > options.maxWidth) {
|
|
||||||
options.width = options.maxWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.height > options.maxHeight) {
|
|
||||||
options.height = options.maxHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
async.waterfall([
|
|
||||||
callback => {
|
|
||||||
if (options.userAgent) {
|
|
||||||
callback();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
inferUserAgent(options.electronVersion, options.platform)
|
|
||||||
.then(userAgent => {
|
|
||||||
options.userAgent = userAgent;
|
|
||||||
callback();
|
|
||||||
})
|
|
||||||
.catch(callback);
|
|
||||||
},
|
|
||||||
callback => {
|
|
||||||
if (options.icon) {
|
|
||||||
callback();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
inferIcon(options.targetUrl, options.platform)
|
|
||||||
.then(pngPath => {
|
|
||||||
options.icon = pngPath;
|
|
||||||
callback();
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
log.warn('Cannot automatically retrieve the app icon:', error);
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
callback => {
|
|
||||||
// length also checks if its the commanderJS function or a string
|
|
||||||
if (options.name && options.name.length > 0) {
|
|
||||||
callback();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
options.name = DEFAULT_APP_NAME;
|
|
||||||
|
|
||||||
inferTitle(options.targetUrl).then(pageTitle => {
|
|
||||||
options.name = pageTitle;
|
|
||||||
}).catch(error => {
|
|
||||||
log.warn(`Unable to automatically determine app name, falling back to '${DEFAULT_APP_NAME}'. Reason: ${error}`);
|
|
||||||
}).then(() => {
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
], error => {
|
|
||||||
callback(error, sanitizeOptions(options));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function sanitizeFilename(platform, str) {
|
|
||||||
let result = sanitizeFilenameLib(str);
|
|
||||||
|
|
||||||
// remove all non ascii or use default app name
|
|
||||||
result = result.replace(/[^\x00-\x7F]/g, '') || DEFAULT_APP_NAME;
|
|
||||||
|
|
||||||
// spaces will cause problems with Ubuntu when pinned to the dock
|
|
||||||
if (platform === 'linux') {
|
|
||||||
return _.kebabCase(result);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function sanitizeOptions(options) {
|
|
||||||
options.name = sanitizeFilename(options.platform, options.name);
|
|
||||||
return options;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default optionsFactory;
|
export default optionsFactory;
|
||||||
|
2
test/module/.eslintrc.yml
Normal file
2
test/module/.eslintrc.yml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
env:
|
||||||
|
mocha: true
|
@ -7,36 +7,36 @@ import os from 'os';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import convertToIcns from './../../lib/helpers/convertToIcns';
|
import convertToIcns from './../../lib/helpers/convertToIcns';
|
||||||
|
|
||||||
let assert = chai.assert;
|
const assert = chai.assert;
|
||||||
|
|
||||||
// Prerequisite for test: to use OSX with sips, iconutil and imagemagick convert
|
// Prerequisite for test: to use OSX with sips, iconutil and imagemagick convert
|
||||||
|
|
||||||
function testConvertPng(pngName, done) {
|
function testConvertPng(pngName, done) {
|
||||||
convertToIcns(path.join(__dirname, '../../', 'test-resources', pngName), (error, icnsPath) => {
|
convertToIcns(path.join(__dirname, '../../', 'test-resources', pngName), (error, icnsPath) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
done(error);
|
done(error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let stat = fs.statSync(icnsPath);
|
const stat = fs.statSync(icnsPath);
|
||||||
assert.isTrue(stat.isFile(), 'Output icns file should be a path');
|
assert.isTrue(stat.isFile(), 'Output icns file should be a path');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('Get Icon Module', function() {
|
describe('Get Icon Module', () => {
|
||||||
it('Can convert icons', function() {
|
it('Can convert icons', () => {
|
||||||
if (os.platform() !== 'darwin') {
|
if (os.platform() !== 'darwin') {
|
||||||
console.warn('Skipping png conversion tests, OSX is required');
|
console.warn('Skipping png conversion tests, OSX is required');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
it('Can convert a rgb png to icns', function(done) {
|
it('Can convert a rgb png to icns', (done) => {
|
||||||
testConvertPng('iconSample.png', done);
|
testConvertPng('iconSample.png', done);
|
||||||
});
|
|
||||||
|
|
||||||
it('Can convert a grey png to icns', function(done) {
|
|
||||||
testConvertPng('iconSampleGrey.png', done);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Can convert a grey png to icns', (done) => {
|
||||||
|
testConvertPng('iconSampleGrey.png', done);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -11,65 +11,65 @@ tmp.setGracefulCleanup();
|
|||||||
const assert = chai.assert;
|
const assert = chai.assert;
|
||||||
|
|
||||||
function checkApp(appPath, inputOptions, callback) {
|
function checkApp(appPath, inputOptions, callback) {
|
||||||
try {
|
try {
|
||||||
let relPathToConfig;
|
let relPathToConfig;
|
||||||
|
|
||||||
switch (inputOptions.platform) {
|
switch (inputOptions.platform) {
|
||||||
case 'darwin':
|
case 'darwin':
|
||||||
relPathToConfig = path.join('google-test-app.app', 'Contents/Resources/app');
|
relPathToConfig = path.join('google-test-app.app', 'Contents/Resources/app');
|
||||||
break;
|
break;
|
||||||
case 'linux':
|
case 'linux':
|
||||||
relPathToConfig = 'resources/app';
|
relPathToConfig = 'resources/app';
|
||||||
break;
|
break;
|
||||||
case 'win32':
|
case 'win32':
|
||||||
relPathToConfig = 'resources/app';
|
relPathToConfig = 'resources/app';
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw 'Unknown app platform';
|
throw new Error('Unknown app platform');
|
||||||
}
|
|
||||||
|
|
||||||
const nativefierConfigPath = path.join(appPath, relPathToConfig, 'nativefier.json');
|
|
||||||
const nativefierConfig = JSON.parse(fs.readFileSync(nativefierConfigPath));
|
|
||||||
|
|
||||||
assert.strictEqual(inputOptions.targetUrl, nativefierConfig.targetUrl, 'Packaged app must have the same targetUrl as the input parameters');
|
|
||||||
// app name is not consistent for linux
|
|
||||||
// assert.strictEqual(inputOptions.appName, nativefierConfig.name, 'Packaged app must have the same name as the input parameters');
|
|
||||||
callback();
|
|
||||||
} catch (exception) {
|
|
||||||
callback(exception);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const nativefierConfigPath = path.join(appPath, relPathToConfig, 'nativefier.json');
|
||||||
|
const nativefierConfig = JSON.parse(fs.readFileSync(nativefierConfigPath));
|
||||||
|
|
||||||
|
assert.strictEqual(inputOptions.targetUrl, nativefierConfig.targetUrl, 'Packaged app must have the same targetUrl as the input parameters');
|
||||||
|
// app name is not consistent for linux
|
||||||
|
// assert.strictEqual(inputOptions.appName, nativefierConfig.name,
|
||||||
|
// 'Packaged app must have the same name as the input parameters');
|
||||||
|
callback();
|
||||||
|
} catch (exception) {
|
||||||
|
callback(exception);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('Nativefier Module', function() {
|
describe('Nativefier Module', function () {
|
||||||
this.timeout(240000);
|
this.timeout(240000);
|
||||||
it('Can build an app from a target url', function(done) {
|
it('Can build an app from a target url', (done) => {
|
||||||
async.eachSeries(PLATFORMS, (platform, callback) => {
|
async.eachSeries(PLATFORMS, (platform, callback) => {
|
||||||
|
const tmpObj = tmp.dirSync({ unsafeCleanup: true });
|
||||||
|
|
||||||
const tmpObj = tmp.dirSync({unsafeCleanup: true});
|
const tmpPath = tmpObj.name;
|
||||||
|
const options = {
|
||||||
|
name: 'google-test-app',
|
||||||
|
targetUrl: 'http://google.com',
|
||||||
|
out: tmpPath,
|
||||||
|
overwrite: true,
|
||||||
|
platform: null,
|
||||||
|
};
|
||||||
|
|
||||||
const tmpPath = tmpObj.name;
|
options.platform = platform;
|
||||||
const options = {
|
nativefier(options, (error, appPath) => {
|
||||||
name: 'google-test-app',
|
if (error) {
|
||||||
targetUrl: 'http://google.com',
|
callback(error);
|
||||||
out: tmpPath,
|
return;
|
||||||
overwrite: true,
|
}
|
||||||
platform: null
|
|
||||||
};
|
|
||||||
|
|
||||||
options.platform = platform;
|
checkApp(appPath, options, (error) => {
|
||||||
nativefier(options, (error, appPath) => {
|
callback(error);
|
||||||
if (error) {
|
|
||||||
callback(error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
checkApp(appPath, options, error => {
|
|
||||||
callback(error);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}, error => {
|
|
||||||
done(error);
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
}, (error) => {
|
||||||
|
done(error);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,49 +1,47 @@
|
|||||||
import inferUserAgent from './../../lib/infer/inferUserAgent';
|
|
||||||
import chai from 'chai';
|
import chai from 'chai';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import inferUserAgent from './../../lib/infer/inferUserAgent';
|
||||||
|
|
||||||
const assert = chai.assert;
|
const assert = chai.assert;
|
||||||
|
|
||||||
const TEST_RESULT = {
|
const TEST_RESULT = {
|
||||||
darwin: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.75 Safari/537.36',
|
darwin: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.75 Safari/537.36',
|
||||||
win32: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.75 Safari/537.36',
|
win32: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.75 Safari/537.36',
|
||||||
linux: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.75 Safari/537.36'
|
linux: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.75 Safari/537.36',
|
||||||
};
|
};
|
||||||
|
|
||||||
function testPlatform(platform) {
|
function testPlatform(platform) {
|
||||||
return inferUserAgent('0.37.1', platform)
|
return inferUserAgent('0.37.1', platform)
|
||||||
.then(userAgent => {
|
.then((userAgent) => {
|
||||||
assert.equal(userAgent, TEST_RESULT[platform], 'Correct user agent should be inferred');
|
assert.equal(userAgent, TEST_RESULT[platform], 'Correct user agent should be inferred');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('Infer User Agent', function() {
|
describe('Infer User Agent', function () {
|
||||||
this.timeout(15000);
|
this.timeout(15000);
|
||||||
it('Can infer userAgent for all platforms', function(done) {
|
it('Can infer userAgent for all platforms', (done) => {
|
||||||
const testPromises = _.keys(TEST_RESULT).map(platform => {
|
const testPromises = _.keys(TEST_RESULT).map(platform => testPlatform(platform));
|
||||||
return testPlatform(platform);
|
Promise
|
||||||
});
|
|
||||||
Promise
|
|
||||||
.all(testPromises)
|
.all(testPromises)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch((error) => {
|
||||||
done(error);
|
done(error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Connection error will still get a user agent', function(done) {
|
it('Connection error will still get a user agent', (done) => {
|
||||||
const TIMEOUT_URL = 'http://www.google.com:81/';
|
const TIMEOUT_URL = 'http://www.google.com:81/';
|
||||||
inferUserAgent('1.6.7', 'darwin', TIMEOUT_URL)
|
inferUserAgent('1.6.7', 'darwin', TIMEOUT_URL)
|
||||||
.then(userAgent => {
|
.then((userAgent) => {
|
||||||
assert.equal(
|
assert.equal(
|
||||||
userAgent,
|
userAgent,
|
||||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',
|
||||||
'Expect default user agent on connection error'
|
'Expect default user agent on connection error',
|
||||||
);
|
);
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
.catch(done);
|
.catch(done);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import normalizeUrl from '../../../src/options/normalizeUrl';
|
|
||||||
import chai from 'chai';
|
import chai from 'chai';
|
||||||
|
import normalizeUrl from '../../../src/options/normalizeUrl';
|
||||||
|
|
||||||
const assert = chai.assert;
|
const assert = chai.assert;
|
||||||
const expect = chai.expect;
|
const expect = chai.expect;
|
||||||
|
|
||||||
describe('Normalize URL', () => {
|
describe('Normalize URL', () => {
|
||||||
|
describe('given a valid URL without a protocol', () => {
|
||||||
describe('given a valid URL without a protocol', () => {
|
it('should allow the url', () => {
|
||||||
it('should allow the url', () => {
|
assert.equal(normalizeUrl('http://www.google.com'), 'http://www.google.com');
|
||||||
assert.equal(normalizeUrl('http://www.google.com'), 'http://www.google.com');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('given a valid URL without a protocol', () => {
|
describe('given a valid URL without a protocol', () => {
|
||||||
it('should allow the url and prepend the HTTP protocol', () => {
|
it('should allow the url and prepend the HTTP protocol', () => {
|
||||||
assert.equal(normalizeUrl('www.google.com'), 'http://www.google.com');
|
assert.equal(normalizeUrl('www.google.com'), 'http://www.google.com');
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('given an invalid URL', () => {
|
describe('given an invalid URL', () => {
|
||||||
it('should throw an exception', () => {
|
it('should throw an exception', () => {
|
||||||
expect(() => normalizeUrl('http://ssddfoo bar')).to.throw('Your Url: "http://ssddfoo bar" is invalid!');
|
expect(() => normalizeUrl('http://ssddfoo bar')).to.throw('Your Url: "http://ssddfoo bar" is invalid!');
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,24 +1,24 @@
|
|||||||
var electronPublicApi = ['electron'];
|
const electronPublicApi = ['electron'];
|
||||||
|
|
||||||
var nodeModules = {};
|
const nodeModules = {};
|
||||||
electronPublicApi.forEach(apiString => {
|
electronPublicApi.forEach((apiString) => {
|
||||||
nodeModules[apiString] = 'commonjs ' + apiString;
|
nodeModules[apiString] = `commonjs ${apiString}`;
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
target: 'node',
|
target: 'node',
|
||||||
output: {
|
output: {
|
||||||
filename: 'main.js'
|
filename: 'main.js',
|
||||||
},
|
},
|
||||||
node: {
|
node: {
|
||||||
global: false,
|
global: false,
|
||||||
__dirname: false
|
__dirname: false,
|
||||||
},
|
},
|
||||||
externals: nodeModules,
|
externals: nodeModules,
|
||||||
module: {
|
module: {
|
||||||
loaders: [
|
loaders: [
|
||||||
{test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader'}
|
{ test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' },
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
devtool: 'source-map'
|
devtool: 'source-map',
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user