mirror of
https://github.com/Llewellynvdm/nativefier.git
synced 2024-12-22 01:58:54 +00:00
Integrate prettier
This commit is contained in:
parent
949dcfadd8
commit
95fc46d38d
@ -13,7 +13,7 @@ insert_final_newline = true
|
||||
[*.{js,py}]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
indent_size = 2
|
||||
|
||||
# 2 space indentation
|
||||
[*.{html,css,less,scss,yml,json}]
|
||||
|
@ -1,11 +1,15 @@
|
||||
extends: airbnb-base
|
||||
env:
|
||||
extends:
|
||||
- airbnb-base
|
||||
- prettier
|
||||
env:
|
||||
# TODO: find out how to turn this on only for src/**/*.test.js files
|
||||
jest: true
|
||||
plugins:
|
||||
- import
|
||||
- prettier
|
||||
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']}]
|
||||
prettier/prettier: "error"
|
||||
|
3
.prettierrc.yaml
Normal file
3
.prettierrc.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
arrowParens: always
|
||||
singleQuote: true
|
||||
trailingComma: all
|
@ -8,7 +8,9 @@ function createLoginWindow(loginCallback) {
|
||||
frame: 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', (event, usernameAndPassword) => {
|
||||
loginCallback(usernameAndPassword[0], usernameAndPassword[1]);
|
||||
|
@ -7,7 +7,12 @@ import createMenu from './../menu/menu';
|
||||
import initContextMenu from './../contextMenu/contextMenu';
|
||||
|
||||
const {
|
||||
isOSX, linkIsInternal, getCssToInject, shouldInjectCss, getAppIcon, nativeTabsSupported,
|
||||
isOSX,
|
||||
linkIsInternal,
|
||||
getCssToInject,
|
||||
shouldInjectCss,
|
||||
getAppIcon,
|
||||
nativeTabsSupported,
|
||||
} = helpers;
|
||||
|
||||
const ZOOM_INTERVAL = 0.1;
|
||||
@ -37,7 +42,10 @@ function maybeInjectCss(browserWindow) {
|
||||
|
||||
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);
|
||||
browserWindow.webContents.removeListener(
|
||||
'did-get-response-details',
|
||||
injectCss,
|
||||
);
|
||||
});
|
||||
|
||||
// on every page navigation inject the css
|
||||
@ -48,7 +56,6 @@ function maybeInjectCss(browserWindow) {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {{}} inpOptions AppArgs from nativefier.json
|
||||
@ -78,24 +85,29 @@ function createMainWindow(inpOptions, onAppQuit, setDockBadge) {
|
||||
},
|
||||
};
|
||||
|
||||
const mainWindow = new BrowserWindow(Object.assign({
|
||||
frame: !options.hideWindowFrame,
|
||||
width: mainWindowState.width,
|
||||
height: mainWindowState.height,
|
||||
minWidth: options.minWidth,
|
||||
minHeight: options.minHeight,
|
||||
maxWidth: options.maxWidth,
|
||||
maxHeight: options.maxHeight,
|
||||
x: options.x,
|
||||
y: options.y,
|
||||
autoHideMenuBar: !options.showMenuBar,
|
||||
// after webpack path here should reference `resources/app/`
|
||||
icon: getAppIcon(),
|
||||
// set to undefined and not false because explicitly setting to false will disable full screen
|
||||
fullscreen: options.fullScreen || undefined,
|
||||
// Whether the window should always stay on top of other windows. Default is false.
|
||||
alwaysOnTop: options.alwaysOnTop,
|
||||
}, DEFAULT_WINDOW_OPTIONS));
|
||||
const mainWindow = new BrowserWindow(
|
||||
Object.assign(
|
||||
{
|
||||
frame: !options.hideWindowFrame,
|
||||
width: mainWindowState.width,
|
||||
height: mainWindowState.height,
|
||||
minWidth: options.minWidth,
|
||||
minHeight: options.minHeight,
|
||||
maxWidth: options.maxWidth,
|
||||
maxHeight: options.maxHeight,
|
||||
x: options.x,
|
||||
y: options.y,
|
||||
autoHideMenuBar: !options.showMenuBar,
|
||||
// after webpack path here should reference `resources/app/`
|
||||
icon: getAppIcon(),
|
||||
// set to undefined and not false because explicitly setting to false will disable full screen
|
||||
fullscreen: options.fullScreen || undefined,
|
||||
// Whether the window should always stay on top of other windows. Default is false.
|
||||
alwaysOnTop: options.alwaysOnTop,
|
||||
},
|
||||
DEFAULT_WINDOW_OPTIONS,
|
||||
),
|
||||
);
|
||||
|
||||
mainWindowState.manage(mainWindow);
|
||||
|
||||
@ -103,12 +115,17 @@ function createMainWindow(inpOptions, onAppQuit, setDockBadge) {
|
||||
if (options.maximize) {
|
||||
mainWindow.maximize();
|
||||
options.maximize = undefined;
|
||||
fs.writeFileSync(path.join(__dirname, '..', 'nativefier.json'), JSON.stringify(options));
|
||||
fs.writeFileSync(
|
||||
path.join(__dirname, '..', 'nativefier.json'),
|
||||
JSON.stringify(options),
|
||||
);
|
||||
}
|
||||
|
||||
const withFocusedWindow = (block) => {
|
||||
const focusedWindow = BrowserWindow.getFocusedWindow();
|
||||
if (focusedWindow) { block(focusedWindow); }
|
||||
if (focusedWindow) {
|
||||
block(focusedWindow);
|
||||
}
|
||||
};
|
||||
|
||||
const adjustWindowZoom = (window, adjustment) => {
|
||||
@ -118,11 +135,15 @@ function createMainWindow(inpOptions, onAppQuit, setDockBadge) {
|
||||
};
|
||||
|
||||
const onZoomIn = () => {
|
||||
withFocusedWindow(focusedWindow => adjustWindowZoom(focusedWindow, ZOOM_INTERVAL));
|
||||
withFocusedWindow((focusedWindow) =>
|
||||
adjustWindowZoom(focusedWindow, ZOOM_INTERVAL),
|
||||
);
|
||||
};
|
||||
|
||||
const onZoomOut = () => {
|
||||
withFocusedWindow(focusedWindow => adjustWindowZoom(focusedWindow, -ZOOM_INTERVAL));
|
||||
withFocusedWindow((focusedWindow) =>
|
||||
adjustWindowZoom(focusedWindow, -ZOOM_INTERVAL),
|
||||
);
|
||||
};
|
||||
|
||||
const onZoomReset = () => {
|
||||
@ -132,23 +153,28 @@ function createMainWindow(inpOptions, onAppQuit, setDockBadge) {
|
||||
};
|
||||
|
||||
const clearAppData = () => {
|
||||
dialog.showMessageBox(mainWindow, {
|
||||
type: 'warning',
|
||||
buttons: ['Yes', 'Cancel'],
|
||||
defaultId: 1,
|
||||
title: 'Clear cache confirmation',
|
||||
message: 'This will clear all data (cookies, local storage etc) from this app. Are you sure you wish to proceed?',
|
||||
}, (response) => {
|
||||
if (response !== 0) {
|
||||
return;
|
||||
}
|
||||
const { session } = mainWindow.webContents;
|
||||
session.clearStorageData(() => {
|
||||
session.clearCache(() => {
|
||||
mainWindow.loadURL(options.targetUrl);
|
||||
dialog.showMessageBox(
|
||||
mainWindow,
|
||||
{
|
||||
type: 'warning',
|
||||
buttons: ['Yes', 'Cancel'],
|
||||
defaultId: 1,
|
||||
title: 'Clear cache confirmation',
|
||||
message:
|
||||
'This will clear all data (cookies, local storage etc) from this app. Are you sure you wish to proceed?',
|
||||
},
|
||||
(response) => {
|
||||
if (response !== 0) {
|
||||
return;
|
||||
}
|
||||
const { session } = mainWindow.webContents;
|
||||
session.clearStorageData(() => {
|
||||
session.clearCache(() => {
|
||||
mainWindow.loadURL(options.targetUrl);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
const onGoBack = () => {
|
||||
@ -236,7 +262,10 @@ function createMainWindow(inpOptions, onAppQuit, setDockBadge) {
|
||||
|
||||
createMenu(menuOptions);
|
||||
if (!options.disableContextMenu) {
|
||||
initContextMenu(createNewWindow, nativeTabsSupported() ? createNewTab : undefined);
|
||||
initContextMenu(
|
||||
createNewWindow,
|
||||
nativeTabsSupported() ? createNewTab : undefined,
|
||||
);
|
||||
}
|
||||
|
||||
if (options.userAgent) {
|
||||
@ -280,7 +309,10 @@ function createMainWindow(inpOptions, onAppQuit, setDockBadge) {
|
||||
mainWindow.moveTabToNewWindow();
|
||||
}
|
||||
mainWindow.setFullScreen(false);
|
||||
mainWindow.once('leave-full-screen', maybeHideWindow.bind(this, mainWindow, event, options.fastQuit));
|
||||
mainWindow.once(
|
||||
'leave-full-screen',
|
||||
maybeHideWindow.bind(this, mainWindow, event, options.fastQuit),
|
||||
);
|
||||
}
|
||||
maybeHideWindow(mainWindow, event, options.fastQuit, options.tray);
|
||||
});
|
||||
|
@ -29,9 +29,10 @@ function createMenu({
|
||||
if (Menu.getApplicationMenu()) {
|
||||
return;
|
||||
}
|
||||
const zoomResetLabel = (zoomBuildTimeValue === 1.0) ?
|
||||
'Reset Zoom' :
|
||||
`Reset Zoom (to ${zoomBuildTimeValue * 100}%, set at build time)`;
|
||||
const zoomResetLabel =
|
||||
zoomBuildTimeValue === 1.0
|
||||
? 'Reset Zoom'
|
||||
: `Reset Zoom (to ${zoomBuildTimeValue * 100}%, set at build time)`;
|
||||
|
||||
const template = [
|
||||
{
|
||||
|
@ -1,8 +1,6 @@
|
||||
import helpers from './../../helpers/helpers';
|
||||
|
||||
const {
|
||||
app, Tray, Menu, ipcMain, nativeImage,
|
||||
} = require('electron');
|
||||
const { app, Tray, Menu, ipcMain, nativeImage } = require('electron');
|
||||
|
||||
const { getAppIcon } = helpers;
|
||||
|
||||
|
@ -47,7 +47,7 @@ function findSync(pattern, basePath, findDir) {
|
||||
matches.push(childPath);
|
||||
}
|
||||
});
|
||||
}(basePath));
|
||||
})(basePath);
|
||||
return matches;
|
||||
}
|
||||
|
||||
@ -56,11 +56,18 @@ function linuxMatch() {
|
||||
}
|
||||
|
||||
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() {
|
||||
return findSync(/PepperFlashPlayer.plugin/, '/Applications/Google Chrome.app/', true)[0];
|
||||
return findSync(
|
||||
/PepperFlashPlayer.plugin/,
|
||||
'/Applications/Google Chrome.app/',
|
||||
true,
|
||||
)[0];
|
||||
}
|
||||
|
||||
function inferFlash() {
|
||||
|
@ -55,11 +55,17 @@ if (appArgs.diskCacheSize) {
|
||||
}
|
||||
|
||||
if (appArgs.basicAuthUsername) {
|
||||
app.commandLine.appendSwitch('basic-auth-username', appArgs.basicAuthUsername);
|
||||
app.commandLine.appendSwitch(
|
||||
'basic-auth-username',
|
||||
appArgs.basicAuthUsername,
|
||||
);
|
||||
}
|
||||
|
||||
if (appArgs.basicAuthPassword) {
|
||||
app.commandLine.appendSwitch('basic-auth-password', appArgs.basicAuthPassword);
|
||||
app.commandLine.appendSwitch(
|
||||
'basic-auth-password',
|
||||
appArgs.basicAuthPassword,
|
||||
);
|
||||
}
|
||||
|
||||
// do nothing for setDockBadge if not OSX
|
||||
@ -126,7 +132,10 @@ app.on('login', (event, webContents, request, authInfo, callback) => {
|
||||
// for http authentication
|
||||
event.preventDefault();
|
||||
|
||||
if (appArgs.basicAuthUsername !== null && appArgs.basicAuthPassword !== null) {
|
||||
if (
|
||||
appArgs.basicAuthUsername !== null &&
|
||||
appArgs.basicAuthPassword !== null
|
||||
) {
|
||||
callback(appArgs.basicAuthUsername, appArgs.basicAuthPassword);
|
||||
} else {
|
||||
createLoginWindow(callback);
|
||||
@ -137,9 +146,12 @@ if (appArgs.singleInstance) {
|
||||
const shouldQuit = app.makeSingleInstance(() => {
|
||||
// Someone tried to run a second instance, we should focus our window.
|
||||
if (mainWindow) {
|
||||
if (!mainWindow.isVisible()) { // tray
|
||||
if (!mainWindow.isVisible()) {
|
||||
// tray
|
||||
mainWindow.show();
|
||||
} if (mainWindow.isMinimized()) { // minimized
|
||||
}
|
||||
if (mainWindow.isMinimized()) {
|
||||
// minimized
|
||||
mainWindow.restore();
|
||||
}
|
||||
mainWindow.focus();
|
||||
|
@ -4,6 +4,9 @@ import PATHS from './../helpers/src-paths';
|
||||
|
||||
const webpackConfig = require('./../../webpack.config.js');
|
||||
|
||||
gulp.task('build-app', ['build-static'], () => gulp.src(PATHS.APP_MAIN_JS)
|
||||
.pipe(webpack(webpackConfig))
|
||||
.pipe(gulp.dest(PATHS.APP_DEST)));
|
||||
gulp.task('build-app', ['build-static'], () =>
|
||||
gulp
|
||||
.src(PATHS.APP_MAIN_JS)
|
||||
.pipe(webpack(webpackConfig))
|
||||
.pipe(gulp.dest(PATHS.APP_DEST)),
|
||||
);
|
||||
|
@ -4,4 +4,6 @@ import helpers from './../helpers/gulp-helpers';
|
||||
|
||||
const { buildES6 } = helpers;
|
||||
|
||||
gulp.task('build-cli', done => buildES6(PATHS.CLI_SRC_JS, PATHS.CLI_DEST, done));
|
||||
gulp.task('build-cli', (done) =>
|
||||
buildES6(PATHS.CLI_SRC_JS, PATHS.CLI_DEST, done),
|
||||
);
|
||||
|
@ -4,9 +4,14 @@ import helpers from './../helpers/gulp-helpers';
|
||||
|
||||
const { buildES6 } = helpers;
|
||||
|
||||
gulp.task('build-static-not-js', () => gulp.src([PATHS.APP_STATIC_ALL, '!**/*.js'])
|
||||
.pipe(gulp.dest(PATHS.APP_STATIC_DEST)));
|
||||
gulp.task('build-static-not-js', () =>
|
||||
gulp
|
||||
.src([PATHS.APP_STATIC_ALL, '!**/*.js'])
|
||||
.pipe(gulp.dest(PATHS.APP_STATIC_DEST)),
|
||||
);
|
||||
|
||||
gulp.task('build-static-js', done => buildES6(PATHS.APP_STATIC_JS, PATHS.APP_STATIC_DEST, done));
|
||||
gulp.task('build-static-js', (done) =>
|
||||
buildES6(PATHS.APP_STATIC_JS, PATHS.APP_STATIC_DEST, done),
|
||||
);
|
||||
|
||||
gulp.task('build-static', ['build-static-js', 'build-static-not-js']);
|
||||
|
@ -14,7 +14,8 @@ function shellExec(cmd, silent, callback) {
|
||||
}
|
||||
|
||||
function buildES6(src, dest, callback) {
|
||||
return gulp.src(src)
|
||||
return gulp
|
||||
.src(src)
|
||||
.pipe(sourcemaps.init())
|
||||
.pipe(babel())
|
||||
.on('error', callback)
|
||||
|
@ -8,4 +8,4 @@ gulp.task('publish', (done) => {
|
||||
shellExec('npm publish', false, done);
|
||||
});
|
||||
|
||||
gulp.task('release', callback => runSequence('build', 'publish', callback));
|
||||
gulp.task('release', (callback) => runSequence('build', 'publish', callback));
|
||||
|
@ -23,7 +23,8 @@
|
||||
"watch": "while true ; do gulp watch ; done",
|
||||
"package-placeholder": "npm run build && node lib/cli.js http://www.bennish.net/web-notifications.html ~/Desktop --overwrite --name notification-test --icon ./test-resources/iconSampleGrey.png --inject ./test-resources/test-injection.js --inject ./test-resources/test-injection.css && open ~/Desktop/notification-test-darwin-x64/notification-test.app",
|
||||
"start-placeholder": "npm run build && electron app",
|
||||
"changelog": "./scripts/changelog"
|
||||
"changelog": "./scripts/changelog",
|
||||
"format": "prettier --write '{gulp,src}/**/*.js' 'app/src/**/*.js'"
|
||||
},
|
||||
"bin": {
|
||||
"nativefier": "lib/cli.js"
|
||||
@ -68,11 +69,14 @@
|
||||
"del": "^3.0.0",
|
||||
"eslint": "^4.17.0",
|
||||
"eslint-config-airbnb-base": "^12.1.0",
|
||||
"eslint-config-prettier": "^2.9.0",
|
||||
"eslint-plugin-import": "^2.8.0",
|
||||
"eslint-plugin-prettier": "^2.6.0",
|
||||
"gulp": "^3.9.1",
|
||||
"gulp-babel": "^7.0.1",
|
||||
"gulp-sourcemaps": "^2.6.4",
|
||||
"jest": "^22.1.4",
|
||||
"prettier": "^1.12.1",
|
||||
"regenerator-runtime": "^0.11.1",
|
||||
"require-dir": "^1.0.0",
|
||||
"run-sequence": "^2.2.1",
|
||||
|
@ -64,30 +64,33 @@ function maybeCopyScripts(srcs, dest) {
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
const promises = srcs.map(src => new Promise((resolve, reject) => {
|
||||
if (!fs.existsSync(src)) {
|
||||
reject(new Error('Error copying injection files: file not found'));
|
||||
return;
|
||||
}
|
||||
const promises = srcs.map(
|
||||
(src) =>
|
||||
new Promise((resolve, reject) => {
|
||||
if (!fs.existsSync(src)) {
|
||||
reject(new Error('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;
|
||||
}
|
||||
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(new Error(`Error Copying injection files: ${error}`));
|
||||
return;
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
}));
|
||||
copy(src, path.join(dest, 'inject', destFileName), (error) => {
|
||||
if (error) {
|
||||
reject(new Error(`Error Copying injection files: ${error}`));
|
||||
return;
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
Promise.all(promises)
|
||||
@ -133,7 +136,10 @@ function buildApp(src, dest, options, callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
fs.writeFileSync(path.join(dest, '/nativefier.json'), JSON.stringify(appArgs));
|
||||
fs.writeFileSync(
|
||||
path.join(dest, '/nativefier.json'),
|
||||
JSON.stringify(appArgs),
|
||||
);
|
||||
|
||||
maybeCopyScripts(options.inject, dest)
|
||||
.catch((err) => {
|
||||
@ -146,5 +152,4 @@ function buildApp(src, dest, options, callback) {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export default buildApp;
|
||||
|
@ -28,7 +28,10 @@ function getAppPath(appPathArray) {
|
||||
}
|
||||
|
||||
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];
|
||||
@ -43,7 +46,9 @@ function maybeNoIconOption(options) {
|
||||
const packageOptions = JSON.parse(JSON.stringify(options));
|
||||
if (options.platform === 'win32' && !isWindows()) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -86,7 +91,9 @@ function removeInvalidOptions(options, param) {
|
||||
const packageOptions = JSON.parse(JSON.stringify(options));
|
||||
if (options.platform === 'win32' && !isWindows()) {
|
||||
if (!hasBinary.sync('wine')) {
|
||||
log.warn(`Wine is required to use "${param}" option for a Windows app when packaging on non-windows platforms`);
|
||||
log.warn(
|
||||
`Wine is required to use "${param}" option for a Windows app when packaging on non-windows platforms`,
|
||||
);
|
||||
packageOptions[param] = null;
|
||||
}
|
||||
}
|
||||
@ -161,74 +168,80 @@ function buildMain(inpOptions, callback) {
|
||||
|
||||
const progress = new DishonestProgress(5);
|
||||
|
||||
async.waterfall([
|
||||
(cb) => {
|
||||
progress.tick('inferring');
|
||||
optionsFactory(options)
|
||||
.then((result) => {
|
||||
cb(null, result);
|
||||
}).catch((error) => {
|
||||
cb(error);
|
||||
async.waterfall(
|
||||
[
|
||||
(cb) => {
|
||||
progress.tick('inferring');
|
||||
optionsFactory(options)
|
||||
.then((result) => {
|
||||
cb(null, result);
|
||||
})
|
||||
.catch((error) => {
|
||||
cb(error);
|
||||
});
|
||||
},
|
||||
(opts, cb) => {
|
||||
progress.tick('copying');
|
||||
buildApp(opts.dir, tmpPath, opts, (error) => {
|
||||
if (error) {
|
||||
cb(error);
|
||||
return;
|
||||
}
|
||||
// Change the reference file for the Electron app to be the temporary path
|
||||
const newOptions = Object.assign({}, opts, {
|
||||
dir: tmpPath,
|
||||
});
|
||||
cb(null, newOptions);
|
||||
});
|
||||
},
|
||||
(opts, cb) => {
|
||||
progress.tick('copying');
|
||||
buildApp(opts.dir, tmpPath, opts, (error) => {
|
||||
if (error) {
|
||||
cb(error);
|
||||
},
|
||||
(opts, cb) => {
|
||||
progress.tick('icons');
|
||||
iconBuild(opts, (error, optionsWithIcon) => {
|
||||
cb(null, optionsWithIcon);
|
||||
});
|
||||
},
|
||||
(opts, cb) => {
|
||||
progress.tick('packaging');
|
||||
// maybe skip passing icon parameter to electron packager
|
||||
let packageOptions = maybeNoIconOption(opts);
|
||||
// maybe skip passing other parameters to electron packager
|
||||
packageOptions = maybeNoAppCopyrightOption(packageOptions);
|
||||
packageOptions = maybeNoAppVersionOption(packageOptions);
|
||||
packageOptions = maybeNoBuildVersionOption(packageOptions);
|
||||
packageOptions = maybeNoVersionStringOption(packageOptions);
|
||||
packageOptions = maybeNoWin32metadataOption(packageOptions);
|
||||
|
||||
packagerConsole.override();
|
||||
|
||||
packager(packageOptions)
|
||||
.then((appPathArray) => {
|
||||
packagerConsole.restore(); // restore console.error
|
||||
cb(null, opts, appPathArray); // options still contain the icon to waterfall
|
||||
})
|
||||
.catch((error) => {
|
||||
packagerConsole.restore(); // restore console.error
|
||||
cb(error, opts); // options still contain the icon to waterfall
|
||||
});
|
||||
},
|
||||
(opts, appPathArray, cb) => {
|
||||
progress.tick('finalizing');
|
||||
// somehow appPathArray is a 1 element array
|
||||
const appPath = getAppPath(appPathArray);
|
||||
if (!appPath) {
|
||||
cb();
|
||||
return;
|
||||
}
|
||||
// Change the reference file for the Electron app to be the temporary path
|
||||
const newOptions = Object.assign({}, opts, { dir: tmpPath });
|
||||
cb(null, newOptions);
|
||||
});
|
||||
},
|
||||
(opts, cb) => {
|
||||
progress.tick('icons');
|
||||
iconBuild(opts, (error, optionsWithIcon) => {
|
||||
cb(null, optionsWithIcon);
|
||||
});
|
||||
},
|
||||
(opts, cb) => {
|
||||
progress.tick('packaging');
|
||||
// maybe skip passing icon parameter to electron packager
|
||||
let packageOptions = maybeNoIconOption(opts);
|
||||
// maybe skip passing other parameters to electron packager
|
||||
packageOptions = maybeNoAppCopyrightOption(packageOptions);
|
||||
packageOptions = maybeNoAppVersionOption(packageOptions);
|
||||
packageOptions = maybeNoBuildVersionOption(packageOptions);
|
||||
packageOptions = maybeNoVersionStringOption(packageOptions);
|
||||
packageOptions = maybeNoWin32metadataOption(packageOptions);
|
||||
|
||||
packagerConsole.override();
|
||||
|
||||
packager(packageOptions)
|
||||
.then((appPathArray) => {
|
||||
packagerConsole.restore(); // restore console.error
|
||||
cb(null, opts, appPathArray); // options still contain the icon to waterfall
|
||||
})
|
||||
.catch((error) => {
|
||||
packagerConsole.restore(); // restore console.error
|
||||
cb(error, opts); // options still contain the icon to waterfall
|
||||
maybeCopyIcons(opts, appPath, (error) => {
|
||||
cb(error, appPath);
|
||||
});
|
||||
},
|
||||
],
|
||||
(error, appPath) => {
|
||||
packagerConsole.playback();
|
||||
callback(error, appPath);
|
||||
},
|
||||
(opts, appPathArray, cb) => {
|
||||
progress.tick('finalizing');
|
||||
// somehow appPathArray is a 1 element array
|
||||
const appPath = getAppPath(appPathArray);
|
||||
if (!appPath) {
|
||||
cb();
|
||||
return;
|
||||
}
|
||||
|
||||
maybeCopyIcons(opts, appPath, (error) => {
|
||||
cb(error, appPath);
|
||||
});
|
||||
},
|
||||
], (error, appPath) => {
|
||||
packagerConsole.playback();
|
||||
callback(error, appPath);
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
export default buildMain;
|
||||
|
@ -84,7 +84,9 @@ function iconBuild(inpOptions, callback) {
|
||||
}
|
||||
|
||||
if (!isOSX()) {
|
||||
log.warn('Skipping icon conversion to .icns, conversion is only supported on OSX');
|
||||
log.warn(
|
||||
'Skipping icon conversion to .icns, conversion is only supported on OSX',
|
||||
);
|
||||
returnCallback();
|
||||
return;
|
||||
}
|
||||
|
171
src/cli.js
171
src/cli.js
@ -28,12 +28,13 @@ function getProcessEnvs(val) {
|
||||
function checkInternet() {
|
||||
dns.lookup('npmjs.com', (err) => {
|
||||
if (err && err.code === 'ENOTFOUND') {
|
||||
log.warn('\nNo Internet Connection\nTo offline build, download electron from https://github.com/electron/electron/releases\nand place in ~/AppData/Local/electron/Cache/ on Windows,\n~/.cache/electron on Linux or ~/Library/Caches/electron/ on Mac\nUse --electron-version to specify the version you downloaded.');
|
||||
log.warn(
|
||||
'\nNo Internet Connection\nTo offline build, download electron from https://github.com/electron/electron/releases\nand place in ~/AppData/Local/electron/Cache/ on Windows,\n~/.cache/electron on Linux or ~/Library/Caches/electron/ on Mac\nUse --electron-version to specify the version you downloaded.',
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (require.main === module) {
|
||||
program
|
||||
.version(packageJson.version)
|
||||
@ -43,51 +44,153 @@ if (require.main === module) {
|
||||
program.out = appDir;
|
||||
})
|
||||
.option('-n, --name <value>', 'app name')
|
||||
.option('-p, --platform <value>', '\'osx\', \'mas\', \'linux\' or \'windows\'')
|
||||
.option('-a, --arch <value>', '\'ia32\' or \'x64\' or \'armv7l\'')
|
||||
.option('--app-version <value>', 'The release version of the application. Maps to the `ProductVersion` metadata property on Windows, and `CFBundleShortVersionString` on OS X.')
|
||||
.option('--build-version <value>', 'The build version of the application. Maps to the `FileVersion` metadata property on Windows, and `CFBundleVersion` on OS X.')
|
||||
.option('--app-copyright <value>', 'The human-readable copyright line for the app. Maps to the `LegalCopyright` metadata property on Windows, and `NSHumanReadableCopyright` on OS X')
|
||||
.option('--win32metadata <json-string>', 'a JSON string of key/value pairs of application metadata (ProductName, InternalName, FileDescription) to embed into the executable (Windows only).', parseJson)
|
||||
.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 persistent counter badge in the dock (macOS only), defaults to false')
|
||||
.option('--bounce', 'if the the dock icon should bounce when counter increases (macOS only), defaults to false')
|
||||
.option('-i, --icon <value>', 'the icon file to use as the icon for the app (should be a .png)')
|
||||
.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('-p, --platform <value>', "'osx', 'mas', 'linux' or 'windows'")
|
||||
.option('-a, --arch <value>', "'ia32' or 'x64' or 'armv7l'")
|
||||
.option(
|
||||
'--app-version <value>',
|
||||
'The release version of the application. Maps to the `ProductVersion` metadata property on Windows, and `CFBundleShortVersionString` on OS X.',
|
||||
)
|
||||
.option(
|
||||
'--build-version <value>',
|
||||
'The build version of the application. Maps to the `FileVersion` metadata property on Windows, and `CFBundleVersion` on OS X.',
|
||||
)
|
||||
.option(
|
||||
'--app-copyright <value>',
|
||||
'The human-readable copyright line for the app. Maps to the `LegalCopyright` metadata property on Windows, and `NSHumanReadableCopyright` on OS X',
|
||||
)
|
||||
.option(
|
||||
'--win32metadata <json-string>',
|
||||
'a JSON string of key/value pairs of application metadata (ProductName, InternalName, FileDescription) to embed into the executable (Windows only).',
|
||||
parseJson,
|
||||
)
|
||||
.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 persistent counter badge in the dock (macOS only), defaults to false',
|
||||
)
|
||||
.option(
|
||||
'--bounce',
|
||||
'if the the dock icon should bounce when counter increases (macOS only), defaults to false',
|
||||
)
|
||||
.option(
|
||||
'-i, --icon <value>',
|
||||
'the icon file to use as the icon for the app (should be a .png)',
|
||||
)
|
||||
.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('--x <value>', 'set window x location', parseInt)
|
||||
.option('--y <value>', 'set window y location', parseInt)
|
||||
.option('-m, --show-menu-bar', 'set menu bar visible, defaults to false')
|
||||
.option('-f, --fast-quit', 'quit app after window close (macOS only), defaults to false')
|
||||
.option(
|
||||
'-f, --fast-quit',
|
||||
'quit app after window close (macOS 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(
|
||||
'--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('--disable-gpu', 'disable hardware acceleration')
|
||||
.option('--ignore-gpu-blacklist', 'allow WebGl apps to work on non supported graphics cards')
|
||||
.option(
|
||||
'--ignore-gpu-blacklist',
|
||||
'allow WebGl apps to work on non supported graphics cards',
|
||||
)
|
||||
.option('--enable-es3-apis', 'force activation of WebGl 2.0')
|
||||
.option('--insecure', 'enable loading of insecure content, defaults to false')
|
||||
.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('--disk-cache-size <value>', 'forces the maximum disk space (in bytes) to be used by the disk cache')
|
||||
.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(
|
||||
'--flash-path <value>',
|
||||
'path to Chrome flash plugin, find it in `Chrome://plugins`',
|
||||
)
|
||||
.option(
|
||||
'--disk-cache-size <value>',
|
||||
'forces the maximum disk space (in bytes) to be used by the disk cache',
|
||||
)
|
||||
.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')
|
||||
.option('--processEnvs <json-string>', 'a JSON string of key/value pairs to be set as environment variables before any browser windows are opened.', getProcessEnvs)
|
||||
.option('--file-download-options <json-string>', 'a JSON string of key/value pairs to be set as file download options. See https://github.com/sindresorhus/electron-dl for available options.', parseJson)
|
||||
.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',
|
||||
)
|
||||
.option(
|
||||
'--processEnvs <json-string>',
|
||||
'a JSON string of key/value pairs to be set as environment variables before any browser windows are opened.',
|
||||
getProcessEnvs,
|
||||
)
|
||||
.option(
|
||||
'--file-download-options <json-string>',
|
||||
'a JSON string of key/value pairs to be set as file download options. See https://github.com/sindresorhus/electron-dl for available options.',
|
||||
parseJson,
|
||||
)
|
||||
.option('--tray', 'allow app to stay in system tray')
|
||||
.option('--basic-auth-username <value>', 'basic http(s) auth username')
|
||||
.option('--basic-auth-password <value>', 'basic http(s) auth password')
|
||||
|
@ -26,22 +26,29 @@ function convertToIcns(pngSrc, icnsDest, callback) {
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
callback(stdOut, pngSrc);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(stdOut, pngSrc);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, icnsDest);
|
||||
});
|
||||
callback(null, icnsDest);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -12,9 +12,10 @@ function isWindows() {
|
||||
}
|
||||
|
||||
function downloadFile(fileUrl) {
|
||||
return axios.get(fileUrl, {
|
||||
responseType: 'arraybuffer',
|
||||
})
|
||||
return axios
|
||||
.get(fileUrl, {
|
||||
responseType: 'arraybuffer',
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response.data) {
|
||||
return null;
|
||||
@ -55,7 +56,9 @@ function allowedIconFormats(platform) {
|
||||
formats.push('.ico');
|
||||
break;
|
||||
default:
|
||||
throw new Error(`function allowedIconFormats error: Unknown platform ${platform}`);
|
||||
throw new Error(
|
||||
`function allowedIconFormats error: Unknown platform ${platform}`,
|
||||
);
|
||||
}
|
||||
return formats;
|
||||
}
|
||||
@ -89,7 +92,9 @@ function allowedIconFormats(platform) {
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Error(`function allowedIconFormats error: Unknown platform ${platform}`);
|
||||
throw new Error(
|
||||
`function allowedIconFormats error: Unknown platform ${platform}`,
|
||||
);
|
||||
}
|
||||
return formats;
|
||||
}
|
||||
|
@ -27,18 +27,22 @@ function iconShellHelper(shellScriptPath, icoSrc, dest) {
|
||||
return;
|
||||
}
|
||||
|
||||
shell.exec(`${shellScriptPath} ${icoSrc} ${dest}`, { silent: true }, (exitCode, stdOut, stdError) => {
|
||||
if (exitCode) {
|
||||
// eslint-disable-next-line prefer-promise-reject-errors
|
||||
reject({
|
||||
stdOut,
|
||||
stdError,
|
||||
});
|
||||
return;
|
||||
}
|
||||
shell.exec(
|
||||
`${shellScriptPath} ${icoSrc} ${dest}`,
|
||||
{ silent: true },
|
||||
(exitCode, stdOut, stdError) => {
|
||||
if (exitCode) {
|
||||
// eslint-disable-next-line prefer-promise-reject-errors
|
||||
reject({
|
||||
stdOut,
|
||||
stdError,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(dest);
|
||||
});
|
||||
resolve(dest);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@ -54,22 +58,40 @@ function getTmpDirPath() {
|
||||
*/
|
||||
|
||||
function singleIco(icoSrc) {
|
||||
return iconShellHelper(SCRIPT_PATHS.singleIco, icoSrc, `${getTmpDirPath()}/icon.ico`);
|
||||
return iconShellHelper(
|
||||
SCRIPT_PATHS.singleIco,
|
||||
icoSrc,
|
||||
`${getTmpDirPath()}/icon.ico`,
|
||||
);
|
||||
}
|
||||
|
||||
function convertToPng(icoSrc) {
|
||||
return iconShellHelper(SCRIPT_PATHS.convertToPng, icoSrc, `${getTmpDirPath()}/icon.png`);
|
||||
return iconShellHelper(
|
||||
SCRIPT_PATHS.convertToPng,
|
||||
icoSrc,
|
||||
`${getTmpDirPath()}/icon.png`,
|
||||
);
|
||||
}
|
||||
|
||||
function convertToIco(icoSrc) {
|
||||
return iconShellHelper(SCRIPT_PATHS.convertToIco, icoSrc, `${getTmpDirPath()}/icon.ico`);
|
||||
return iconShellHelper(
|
||||
SCRIPT_PATHS.convertToIco,
|
||||
icoSrc,
|
||||
`${getTmpDirPath()}/icon.ico`,
|
||||
);
|
||||
}
|
||||
|
||||
function convertToIcns(icoSrc) {
|
||||
if (!isOSX()) {
|
||||
return new Promise((resolve, reject) => reject(new Error('OSX is required to convert to a .icns icon')));
|
||||
return new Promise((resolve, reject) =>
|
||||
reject(new Error('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 {
|
||||
|
@ -25,31 +25,30 @@ function getMaxMatchScore(iconWithScores) {
|
||||
*/
|
||||
function getMatchingIcons(iconsWithScores, maxScore) {
|
||||
return iconsWithScores
|
||||
.filter(item => item.score === maxScore)
|
||||
.map(item => Object.assign({}, item, { ext: path.extname(item.url) }));
|
||||
.filter((item) => item.score === maxScore)
|
||||
.map((item) => Object.assign({}, item, { ext: path.extname(item.url) }));
|
||||
}
|
||||
|
||||
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 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 });
|
||||
});
|
||||
return Object.assign({}, item, { score });
|
||||
});
|
||||
}
|
||||
|
||||
function inferIconFromStore(targetUrl, platform) {
|
||||
const allowedFormats = new Set(allowedIconFormats(platform));
|
||||
|
||||
return gitCloud('https://jiahaog.github.io/nativefier-icons/')
|
||||
.then((fileIndex) => {
|
||||
return gitCloud('https://jiahaog.github.io/nativefier-icons/').then(
|
||||
(fileIndex) => {
|
||||
const iconWithScores = mapIconWithMatchScore(fileIndex, targetUrl);
|
||||
const maxScore = getMaxMatchScore(iconWithScores);
|
||||
|
||||
@ -58,7 +57,9 @@ function inferIconFromStore(targetUrl, platform) {
|
||||
}
|
||||
|
||||
const iconsMatchingScore = getMatchingIcons(iconWithScores, maxScore);
|
||||
const iconsMatchingExt = iconsMatchingScore.filter(icon => allowedFormats.has(icon.ext));
|
||||
const iconsMatchingExt = iconsMatchingScore.filter((icon) =>
|
||||
allowedFormats.has(icon.ext),
|
||||
);
|
||||
const matchingIcon = iconsMatchingExt[0];
|
||||
const iconUrl = matchingIcon && matchingIcon.url;
|
||||
|
||||
@ -66,7 +67,8 @@ function inferIconFromStore(targetUrl, platform) {
|
||||
return null;
|
||||
}
|
||||
return downloadFile(iconUrl);
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function writeFilePromise(outPath, data) {
|
||||
@ -88,15 +90,14 @@ function inferFromPage(targetUrl, platform, outDir) {
|
||||
}
|
||||
|
||||
// todo might want to pass list of preferences instead
|
||||
return pageIcon(targetUrl, { ext: preferredExt })
|
||||
.then((icon) => {
|
||||
if (!icon) {
|
||||
return null;
|
||||
}
|
||||
return pageIcon(targetUrl, { ext: preferredExt }).then((icon) => {
|
||||
if (!icon) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const outfilePath = path.join(outDir, `/icon${icon.ext}`);
|
||||
return writeFilePromise(outfilePath, icon.data);
|
||||
});
|
||||
const outfilePath = path.join(outDir, `/icon${icon.ext}`);
|
||||
return writeFilePromise(outfilePath, icon.data);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -106,15 +107,14 @@ function inferFromPage(targetUrl, platform, outDir) {
|
||||
* @param {string} outDir
|
||||
*/
|
||||
function inferIconFromUrlToPath(targetUrl, platform, outDir) {
|
||||
return inferIconFromStore(targetUrl, platform)
|
||||
.then((icon) => {
|
||||
if (!icon) {
|
||||
return inferFromPage(targetUrl, platform, outDir);
|
||||
}
|
||||
return inferIconFromStore(targetUrl, platform).then((icon) => {
|
||||
if (!icon) {
|
||||
return inferFromPage(targetUrl, platform, outDir);
|
||||
}
|
||||
|
||||
const outfilePath = path.join(outDir, `/icon${icon.ext}`);
|
||||
return writeFilePromise(outfilePath, icon.data);
|
||||
});
|
||||
const outfilePath = path.join(outDir, `/icon${icon.ext}`);
|
||||
return writeFilePromise(outfilePath, icon.data);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2,7 +2,12 @@ import os from 'os';
|
||||
|
||||
function inferPlatform() {
|
||||
const platform = os.platform();
|
||||
if ((platform === 'darwin' || platform === 'mas') || platform === 'win32' || platform === 'linux') {
|
||||
if (
|
||||
platform === 'darwin' ||
|
||||
platform === 'mas' ||
|
||||
platform === 'win32' ||
|
||||
platform === 'linux'
|
||||
) {
|
||||
return platform;
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
import axios from 'axios';
|
||||
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) {
|
||||
const options = {
|
||||
@ -15,7 +16,10 @@ function inferTitle(url) {
|
||||
|
||||
return axios(options).then(({ data }) => {
|
||||
const $ = cheerio.load(data);
|
||||
return $('title').first().text().replace(/\//g, '');
|
||||
return $('title')
|
||||
.first()
|
||||
.text()
|
||||
.replace(/\//g, '');
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -5,25 +5,29 @@ import log from 'loglevel';
|
||||
const ELECTRON_VERSIONS_URL = 'https://atom.io/download/atom-shell/index.json';
|
||||
const DEFAULT_CHROME_VERSION = '58.0.3029.110';
|
||||
|
||||
function getChromeVersionForElectronVersion(electronVersion, url = ELECTRON_VERSIONS_URL) {
|
||||
return axios.get(url, { timeout: 5000 })
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
throw new Error(`Bad request: Status code ${response.status}`);
|
||||
}
|
||||
function getChromeVersionForElectronVersion(
|
||||
electronVersion,
|
||||
url = ELECTRON_VERSIONS_URL,
|
||||
) {
|
||||
return axios.get(url, { timeout: 5000 }).then((response) => {
|
||||
if (response.status !== 200) {
|
||||
throw new Error(`Bad request: Status code ${response.status}`);
|
||||
}
|
||||
|
||||
const { data } = response;
|
||||
const electronVersionToChromeVersion = _.zipObject(
|
||||
data.map(d => d.version),
|
||||
data.map(d => d.chrome),
|
||||
const { data } = response;
|
||||
const electronVersionToChromeVersion = _.zipObject(
|
||||
data.map((d) => d.version),
|
||||
data.map((d) => d.chrome),
|
||||
);
|
||||
|
||||
if (!(electronVersion in electronVersionToChromeVersion)) {
|
||||
throw new Error(
|
||||
`Electron version '${electronVersion}' not found in retrieved version list!`,
|
||||
);
|
||||
}
|
||||
|
||||
if (!(electronVersion in electronVersionToChromeVersion)) {
|
||||
throw new Error(`Electron version '${electronVersion}' not found in retrieved version list!`);
|
||||
}
|
||||
|
||||
return electronVersionToChromeVersion[electronVersion];
|
||||
});
|
||||
return electronVersionToChromeVersion[electronVersion];
|
||||
});
|
||||
}
|
||||
|
||||
export function getUserAgentString(chromeVersion, platform) {
|
||||
@ -40,16 +44,24 @@ export function getUserAgentString(chromeVersion, platform) {
|
||||
userAgent = `Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${chromeVersion} Safari/537.36`;
|
||||
break;
|
||||
default:
|
||||
throw new Error('Error invalid platform specified to getUserAgentString()');
|
||||
throw new Error(
|
||||
'Error invalid platform specified to getUserAgentString()',
|
||||
);
|
||||
}
|
||||
return userAgent;
|
||||
}
|
||||
|
||||
function inferUserAgent(electronVersion, platform, url = ELECTRON_VERSIONS_URL) {
|
||||
function inferUserAgent(
|
||||
electronVersion,
|
||||
platform,
|
||||
url = ELECTRON_VERSIONS_URL,
|
||||
) {
|
||||
return getChromeVersionForElectronVersion(electronVersion, url)
|
||||
.then(chromeVersion => getUserAgentString(chromeVersion, platform))
|
||||
.then((chromeVersion) => getUserAgentString(chromeVersion, platform))
|
||||
.catch(() => {
|
||||
log.warn(`Unable to infer chrome version for user agent, using ${DEFAULT_CHROME_VERSION}`);
|
||||
log.warn(
|
||||
`Unable to infer chrome version for user agent, using ${DEFAULT_CHROME_VERSION}`,
|
||||
);
|
||||
return getUserAgentString(DEFAULT_CHROME_VERSION, platform);
|
||||
});
|
||||
}
|
||||
|
@ -20,7 +20,9 @@ function testPlatform(platform) {
|
||||
|
||||
describe('Infer User Agent', () => {
|
||||
test('Can infer userAgent for all platforms', (done) => {
|
||||
const testPromises = _.keys(TEST_RESULT).map(platform => testPlatform(platform));
|
||||
const testPromises = _.keys(TEST_RESULT).map((platform) =>
|
||||
testPlatform(platform),
|
||||
);
|
||||
Promise.all(testPromises)
|
||||
.then(() => {
|
||||
done();
|
||||
@ -36,7 +38,9 @@ describe('Infer User Agent', () => {
|
||||
const TIMEOUT_URL = 'http://www.google.com:81/';
|
||||
inferUserAgent('1.6.7', 'darwin', TIMEOUT_URL)
|
||||
.then((userAgent) => {
|
||||
expect(userAgent).toBe('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36');
|
||||
expect(userAgent).toBe(
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',
|
||||
);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
|
@ -1,7 +1,10 @@
|
||||
import fields from './fields';
|
||||
|
||||
function resultArrayToObject(fieldResults) {
|
||||
return fieldResults.reduce((accumulator, value) => Object.assign({}, accumulator, value), {});
|
||||
return fieldResults.reduce(
|
||||
(accumulator, value) => Object.assign({}, accumulator, value),
|
||||
{},
|
||||
);
|
||||
}
|
||||
|
||||
function inferredOptions(oldOptions, fieldResults) {
|
||||
@ -11,8 +14,9 @@ function inferredOptions(oldOptions, fieldResults) {
|
||||
|
||||
// Takes the options object and infers new values
|
||||
// which may need async work
|
||||
export default function (options) {
|
||||
export default function(options) {
|
||||
const tasks = fields(options);
|
||||
return Promise.all(tasks)
|
||||
.then(fieldResults => inferredOptions(options, fieldResults));
|
||||
return Promise.all(tasks).then((fieldResults) =>
|
||||
inferredOptions(options, fieldResults),
|
||||
);
|
||||
}
|
||||
|
@ -3,9 +3,11 @@ import fields from './fields';
|
||||
|
||||
jest.mock('./fields');
|
||||
|
||||
fields.mockImplementation(() => [Promise.resolve({
|
||||
someField: 'newValue',
|
||||
})]);
|
||||
fields.mockImplementation(() => [
|
||||
Promise.resolve({
|
||||
someField: 'newValue',
|
||||
}),
|
||||
]);
|
||||
|
||||
test('it should merge the result of the promise', () => {
|
||||
const param = { another: 'field', someField: 'oldValue' };
|
||||
@ -15,4 +17,3 @@ test('it should merge the result of the promise', () => {
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1,15 +1,14 @@
|
||||
import log from 'loglevel';
|
||||
import { inferIcon } from './../../infer';
|
||||
|
||||
export default function ({ icon, targetUrl, platform }) {
|
||||
export default function({ icon, targetUrl, platform }) {
|
||||
// Icon is the path to the icon
|
||||
if (icon) {
|
||||
return Promise.resolve(icon);
|
||||
}
|
||||
|
||||
return inferIcon(targetUrl, platform)
|
||||
.catch((error) => {
|
||||
log.warn('Cannot automatically retrieve the app icon:', error);
|
||||
return null;
|
||||
});
|
||||
return inferIcon(targetUrl, platform).catch((error) => {
|
||||
log.warn('Cannot automatically retrieve the app icon:', error);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
@ -29,12 +29,17 @@ describe('when the icon parameter is not passed', () => {
|
||||
|
||||
describe('when inferIcon resolves with an error', () => {
|
||||
test('it should handle the error', () => {
|
||||
inferIcon.mockImplementationOnce(() => Promise.reject(new Error('some error')));
|
||||
inferIcon.mockImplementationOnce(() =>
|
||||
Promise.reject(new Error('some error')),
|
||||
);
|
||||
const params = { targetUrl: 'some url', platform: 'mac' };
|
||||
|
||||
return icon(params).then((result) => {
|
||||
expect(result).toBe(null);
|
||||
expect(inferIcon).toHaveBeenCalledWith(params.targetUrl, params.platform);
|
||||
expect(inferIcon).toHaveBeenCalledWith(
|
||||
params.targetUrl,
|
||||
params.platform,
|
||||
);
|
||||
expect(log.warn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
@ -2,28 +2,31 @@ import icon from './icon';
|
||||
import userAgent from './userAgent';
|
||||
import name from './name';
|
||||
|
||||
const fields = [{
|
||||
field: 'userAgent',
|
||||
task: userAgent,
|
||||
}, {
|
||||
field: 'icon',
|
||||
task: icon,
|
||||
}, {
|
||||
field: 'name',
|
||||
task: name,
|
||||
}];
|
||||
const fields = [
|
||||
{
|
||||
field: 'userAgent',
|
||||
task: userAgent,
|
||||
},
|
||||
{
|
||||
field: 'icon',
|
||||
task: icon,
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
task: name,
|
||||
},
|
||||
];
|
||||
|
||||
// Modifies the result of each promise from a scalar
|
||||
// value to a object containing its fieldname
|
||||
function wrap(fieldName, promise, args) {
|
||||
return promise(args)
|
||||
.then(result => ({
|
||||
[fieldName]: result,
|
||||
}));
|
||||
return promise(args).then((result) => ({
|
||||
[fieldName]: result,
|
||||
}));
|
||||
}
|
||||
|
||||
// Returns a list of promises which will all resolve
|
||||
// with the following result: {[fieldName]: fieldvalue}
|
||||
export default function (options) {
|
||||
export default function(options) {
|
||||
return fields.map(({ field, task }) => wrap(field, task, options));
|
||||
}
|
||||
|
@ -19,4 +19,3 @@ test('it should return a list of promises', () => {
|
||||
expect(value).toBeInstanceOf(Promise);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -10,14 +10,17 @@ function tryToInferName({ name, targetUrl }) {
|
||||
}
|
||||
|
||||
return inferTitle(targetUrl)
|
||||
.then(pageTitle => (pageTitle || DEFAULT_APP_NAME))
|
||||
.then((pageTitle) => pageTitle || DEFAULT_APP_NAME)
|
||||
.catch((error) => {
|
||||
log.warn(`Unable to automatically determine app name, falling back to '${DEFAULT_APP_NAME}'. Reason: ${error}`);
|
||||
log.warn(
|
||||
`Unable to automatically determine app name, falling back to '${DEFAULT_APP_NAME}'. Reason: ${error}`,
|
||||
);
|
||||
return DEFAULT_APP_NAME;
|
||||
});
|
||||
}
|
||||
|
||||
export default function ({ platform, name, targetUrl }) {
|
||||
return tryToInferName({ name, targetUrl })
|
||||
.then(result => sanitizeFilename(platform, result));
|
||||
export default function({ platform, name, targetUrl }) {
|
||||
return tryToInferName({ name, targetUrl }).then((result) =>
|
||||
sanitizeFilename(platform, result),
|
||||
);
|
||||
}
|
||||
|
@ -14,14 +14,16 @@ const mockedResult = 'mock name';
|
||||
|
||||
describe('well formed name parameters', () => {
|
||||
const params = { name: 'appname', platform: 'something' };
|
||||
test('it should not call inferTitle', () => name(params).then((result) => {
|
||||
expect(inferTitle).toHaveBeenCalledTimes(0);
|
||||
expect(result).toBe(params.name);
|
||||
}));
|
||||
test('it should not call inferTitle', () =>
|
||||
name(params).then((result) => {
|
||||
expect(inferTitle).toHaveBeenCalledTimes(0);
|
||||
expect(result).toBe(params.name);
|
||||
}));
|
||||
|
||||
test('it should call sanitize filename', () => name(params).then((result) => {
|
||||
expect(sanitizeFilename).toHaveBeenCalledWith(params.platform, result);
|
||||
}));
|
||||
test('it should call sanitize filename', () =>
|
||||
name(params).then((result) => {
|
||||
expect(sanitizeFilename).toHaveBeenCalledWith(params.platform, result);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('bad name parameters', () => {
|
||||
@ -31,9 +33,10 @@ describe('bad name parameters', () => {
|
||||
|
||||
const params = { targetUrl: 'some url' };
|
||||
describe('when the name is undefined', () => {
|
||||
test('it should call inferTitle', () => name(params).then(() => {
|
||||
expect(inferTitle).toHaveBeenCalledWith(params.targetUrl);
|
||||
}));
|
||||
test('it should call inferTitle', () =>
|
||||
name(params).then(() => {
|
||||
expect(inferTitle).toHaveBeenCalledWith(params.targetUrl);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('when the name is an empty string', () => {
|
||||
@ -46,9 +49,10 @@ describe('bad name parameters', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('it should call sanitize filename', () => name(params).then((result) => {
|
||||
expect(sanitizeFilename).toHaveBeenCalledWith(params.platform, result);
|
||||
}));
|
||||
test('it should call sanitize filename', () =>
|
||||
name(params).then((result) => {
|
||||
expect(sanitizeFilename).toHaveBeenCalledWith(params.platform, result);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('handling inferTitle results', () => {
|
||||
@ -75,7 +79,9 @@ describe('handling inferTitle results', () => {
|
||||
|
||||
describe('when inferTitle resolves with an error', () => {
|
||||
test('it should return the default app name', () => {
|
||||
inferTitle.mockImplementationOnce(() => Promise.reject(new Error('some error')));
|
||||
inferTitle.mockImplementationOnce(() =>
|
||||
Promise.reject(new Error('some error')),
|
||||
);
|
||||
|
||||
return name(params).then((result) => {
|
||||
expect(result).toBe(DEFAULT_APP_NAME);
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { inferUserAgent } from './../../infer';
|
||||
|
||||
export default function ({ userAgent, electronVersion, platform }) {
|
||||
export default function({ userAgent, electronVersion, platform }) {
|
||||
if (userAgent) {
|
||||
return Promise.resolve(userAgent);
|
||||
}
|
||||
|
@ -13,6 +13,8 @@ test('when a userAgent parameter is passed', () => {
|
||||
test('no userAgent parameter is passed', () => {
|
||||
const params = { electronVersion: '123', platform: 'mac' };
|
||||
userAgent(params);
|
||||
expect(inferUserAgent).toHaveBeenCalledWith(params.electronVersion, params.platform);
|
||||
expect(inferUserAgent).toHaveBeenCalledWith(
|
||||
params.electronVersion,
|
||||
params.platform,
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
import normalizeUrl from './normalizeUrl';
|
||||
|
||||
test("a proper URL shouldn't be mangled", () => {
|
||||
expect(normalizeUrl('http://www.google.com')).toEqual('http://www.google.com');
|
||||
expect(normalizeUrl('http://www.google.com')).toEqual(
|
||||
'http://www.google.com',
|
||||
);
|
||||
});
|
||||
|
||||
test('missing protocol should default to http', () => {
|
||||
@ -9,5 +11,7 @@ test('missing protocol should default to http', () => {
|
||||
});
|
||||
|
||||
test("a proper URL shouldn't be mangled", () => {
|
||||
expect(() => { normalizeUrl('http://ssddfoo bar'); }).toThrow('Your Url: "http://ssddfoo bar" is invalid!');
|
||||
expect(() => {
|
||||
normalizeUrl('http://ssddfoo bar');
|
||||
}).toThrow('Your Url: "http://ssddfoo bar" is invalid!');
|
||||
});
|
||||
|
@ -13,7 +13,7 @@ const { inferPlatform, inferArch } = inferOs;
|
||||
* @param {Object} inpOptions
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export default function (inpOptions) {
|
||||
export default function(inpOptions) {
|
||||
const options = {
|
||||
dir: PLACEHOLDER_APP_DIR,
|
||||
name: inpOptions.name,
|
||||
@ -93,7 +93,10 @@ export default function (inpOptions) {
|
||||
options.platform = 'win32';
|
||||
}
|
||||
|
||||
if (options.platform.toLowerCase() === 'osx' || options.platform.toLowerCase() === 'mac') {
|
||||
if (
|
||||
options.platform.toLowerCase() === 'osx' ||
|
||||
options.platform.toLowerCase() === 'mac'
|
||||
) {
|
||||
options.platform = 'darwin';
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ import _ from 'lodash';
|
||||
import sanitizeFilenameLib from 'sanitize-filename';
|
||||
import { DEFAULT_APP_NAME } from './../constants';
|
||||
|
||||
export default function (platform, str) {
|
||||
export default function(platform, str) {
|
||||
let result = sanitizeFilenameLib(str);
|
||||
|
||||
// remove all non ascii or use default app name
|
||||
|
@ -3,7 +3,7 @@ import sanitizeFilename from './sanitizeFilename';
|
||||
import { DEFAULT_APP_NAME } from './../constants';
|
||||
|
||||
jest.mock('sanitize-filename');
|
||||
sanitizeFilenameLib.mockImplementation(str => str);
|
||||
sanitizeFilenameLib.mockImplementation((str) => str);
|
||||
|
||||
test('it should call the sanitize-filename npm module', () => {
|
||||
const param = 'abc';
|
||||
|
Loading…
Reference in New Issue
Block a user