Added import from URI ability (#160)

* Added import from URI ability

* Add protocol handlers

* Fix for keymap setting on read-only mode

* Refactored some main components out

* Protocols shoudl work now
This commit is contained in:
Gregory Schier 2017-05-03 10:48:23 -07:00 committed by GitHub
parent 47e1dccebc
commit 7b8d26c292
25 changed files with 801 additions and 650 deletions

4
.npmrc
View File

@ -1,4 +0,0 @@
runtime=electron
target=1.6.5
target_arch=x64
disturl=https://atom.io/download/atom-shell

View File

@ -31,8 +31,9 @@ Development on Insomnia can be done on Windows, Mac, and Linux with few requirem
requires [NodeJS 7.4](https://nodejs.org) and [Git](https://git-scm.com/) to get started.
```bash
# Install dependencies
# Install dependencies and build addons for Electron
npm install
npm run rebuild
# Start app
npm run dev

View File

@ -0,0 +1,12 @@
import * as appPackage from '../package.json';
import * as globalPackage from '../../package.json';
describe('package.json', () => {
it('all app dependencies should be same in global', () => {
for (const name of Object.keys(appPackage.dependencies)) {
const expected = globalPackage.dependencies[name];
const actual = appPackage.dependencies[name];
expect(`${name}::${actual}`).toBe(`${name}::${expected}`);
}
});
});

View File

@ -26,6 +26,14 @@ export function isMac () {
return getAppPlatform() === 'darwin';
}
export function isLinux () {
return getAppPlatform() === 'linux';
}
export function isWindows () {
return getAppPlatform() === 'win32';
}
export function isDevelopment () {
return getAppEnvironment() === 'development';
}
@ -48,6 +56,7 @@ export const CHANGELOG_PAGE = 'https://insomnia.rest/changelog/';
export const STATUS_CODE_RENDER_FAILED = -333;
export const LARGE_RESPONSE_MB = 5;
export const FLEXIBLE_URL_REGEX = /^(http|https):\/\/[0-9a-zA-Z\-_.]+[/\w.\-+=:\][@%^*&!#?;]*/;
export const CHECK_FOR_UPDATES_INTERVAL = 1000 * 60 * 60 * 3; // 3 hours
// Hotkeys
export const MOD_SYM = isMac() ? '⌘' : 'ctrl';

View File

@ -3,6 +3,11 @@ import * as db from './database';
import * as models from '../models';
import {getAppVersion} from './constants';
import * as misc from './misc';
import {showModal} from '../ui/components/modals/index';
import AlertModal from '../ui/components/modals/alert-modal';
import * as fetch from './fetch';
import fs from 'fs';
import {trackEvent} from '../analytics/index';
const EXPORT_FORMAT = 3;
@ -23,6 +28,43 @@ const MODELS = {
[EXPORT_TYPE_ENVIRONMENT]: models.environment
};
export async function importUri (workspaceId, uri) {
let rawText;
if (uri.match(/^(http|https):\/\//)) {
const response = await fetch.rawFetch(uri);
rawText = await response.text();
} else if (uri.match(/^(file):\/\//)) {
const path = uri.replace(/^(file):\/\//, '');
rawText = fs.readFileSync(path, 'utf8');
} else {
throw new Error(`Invalid import URI ${uri}`);
}
const workspace = await models.workspace.getById(workspaceId);
const result = await importRaw(workspace, rawText);
const {summary, source, error} = result;
if (error) {
showModal(AlertModal, {title: 'Import Failed', message: error});
return;
}
let statements = Object.keys(summary).map(type => {
const count = summary[type].length;
const name = models.getModelName(type, count);
return count === 0 ? null : `${count} ${name}`;
}).filter(s => s !== null);
let message;
if (statements.length === 0) {
message = 'Nothing was found to import.';
} else {
message = `You imported ${statements.join(', ')}!`;
}
showModal(AlertModal, {title: 'Import Succeeded', message});
trackEvent('Import', 'Success', source);
}
export async function importRaw (workspace, rawContent, generateNewIds = false) {
let results;
try {

View File

@ -92,6 +92,9 @@ export function prettifyJson (json, indentChars) {
}
newJson += currentChar;
break;
case '\r':
// Skip windows return characters
break;
default:
newJson += currentChar;
break;

View File

@ -1,192 +1,39 @@
import reboot from 'electron-squirrel-startup';
if (reboot) {
import needsRestart from 'electron-squirrel-startup';
import electron from 'electron';
import {isDevelopment, isMac} from './common/constants';
import * as errorHandling from './main/error-handling';
import * as updates from './main/updates';
import {createWindow} from './main/window-utils';
// Handle potential auto-update
if (needsRestart) {
process.exit(0);
}
import fs from 'fs';
import Raven from 'raven';
import path from 'path';
import electron from 'electron';
import * as packageJSON from './package.json';
import LocalStorage from './common/local-storage';
// Initialize some things
errorHandling.init();
updates.init();
// Some useful helpers
const IS_DEV = process.env.INSOMNIA_ENV === 'development';
const IS_MAC = process.platform === 'darwin';
const IS_WINDOWS = process.platform === 'win32';
const IS_LINUX = !IS_MAC && !IS_WINDOWS;
const ravenClient = Raven.config('https://786e43ae199c4757a9ea4a48a9abd17d@sentry.io/109702', {
environment: process.env.INSOMNIA_ENV || 'production',
release: packageJSON.version,
logger: 'electron.main'
});
if (!IS_DEV) {
ravenClient.install();
function addUrlToOpen (e, url) {
e.preventDefault();
args.push(url);
}
const {app, dialog, shell, ipcMain, autoUpdater, Menu, BrowserWindow} = electron;
const {version: appVersion, productName: appName} = packageJSON;
const {app, ipcMain} = electron;
const UPDATE_URLS = {
// Add `r` param to help cache bust
darwin: `https://updates.insomnia.rest/builds/check/mac?v=${appVersion}`,
linux: `https://updates.insomnia.rest/builds/check/linux?v=${appVersion}`,
win32: `https://downloads.insomnia.rest/win`
};
const args = process.argv.slice(1);
let localStorage = null;
// Set as default protocol
app.setAsDefaultProtocolClient(`insomnia${isDevelopment() ? 'dev' : ''}`);
let mainWindow = null;
let hasPromptedForUpdates = false;
app.on('open-url', addUrlToOpen);
// Enable this for CSS grid layout :)
app.commandLine.appendSwitch('enable-experimental-web-platform-features');
process.on('uncaughtException', e => {
if (IS_DEV) {
console.error(e);
} else {
ravenClient.captureError(e, {});
}
});
autoUpdater.on('error', e => {
if (IS_DEV) {
console.error(e);
} else {
// Don't report autoUpdater error. They are way too noisy
}
});
autoUpdater.on('update-not-available', () => {
console.log('-- Update Not Available --');
});
autoUpdater.on('update-available', () => {
console.log('-- Update Available --');
});
autoUpdater.on('update-downloaded', (e, releaseNotes, releaseName, releaseDate, updateUrl) => {
console.log(`-- Update Downloaded ${releaseName} --`);
showUpdateNotification();
});
function checkForUpdates () {
if (hasPromptedForUpdates) {
// We've already prompted for updates. Don't bug the user anymore
return;
}
if (IS_DEV) {
console.log('-- Skipping update check in Development --');
return;
}
if (!IS_LINUX) {
try {
autoUpdater.setFeedURL(UPDATE_URLS[process.platform]);
autoUpdater.checkForUpdates();
} catch (e) {
// This will fail in development
}
}
}
function showUnresponsiveModal () {
dialog.showMessageBox({
type: 'info',
buttons: ['Cancel', 'Reload'],
defaultId: 1,
cancelId: 0,
title: 'Unresponsive',
message: 'Insomnia has become unresponsive. Do you want to reload?'
}, id => {
if (id === 1) {
mainWindow.destroy();
createWindow();
}
});
}
function showUpdateNotification () {
if (hasPromptedForUpdates) {
return;
}
const window = BrowserWindow.getFocusedWindow();
if (!window || !window.webContents) {
return;
}
window.webContents.send('update-available');
hasPromptedForUpdates = true;
}
function trackEvent (...args) {
const window = BrowserWindow.getFocusedWindow();
if (!window || !window.webContents) {
return;
}
window.webContents.send('analytics-track-event', args);
}
ipcMain.on('check-for-updates', () => {
console.log('-- Checking for Updates --');
checkForUpdates();
});
function saveBounds () {
if (!mainWindow) {
return;
}
const fullscreen = mainWindow.isFullScreen();
// Only save the size if we're not in fullscreen
if (!fullscreen) {
localStorage.setItem('bounds', mainWindow.getBounds());
localStorage.setItem('fullscreen', false);
} else {
localStorage.setItem('fullscreen', true);
}
}
function getBounds () {
let bounds = {};
let fullscreen = false;
try {
bounds = localStorage.getItem('bounds', {});
fullscreen = localStorage.getItem('fullscreen', false);
} catch (e) {
// This should never happen, but if it does...!
console.error('Failed to parse window bounds', e);
}
return {bounds, fullscreen};
}
function saveZoomFactor (zoomFactor) {
localStorage.setItem('zoomFactor', zoomFactor);
}
function getZoomFactor () {
let zoomFactor = 1;
try {
zoomFactor = localStorage.getItem('zoomFactor', 1);
} catch (e) {
// This should never happen, but if it does...!
console.error('Failed to parse zoomFactor', e);
}
return zoomFactor;
}
// Quit when all windows are closed (except on Mac).
app.on('window-all-closed', () => {
if (!IS_MAC) {
if (!isMac()) {
app.quit();
}
});
@ -207,307 +54,26 @@ app.on('activate', (e, hasVisibleWindows) => {
// When the app is first launched
app.on('ready', () => {
initLocalStorage();
createWindow();
checkForUpdates();
app.removeListener('open-url', addUrlToOpen);
const window = createWindow();
// Handle URLs sent via command line args
ipcMain.once('app-ready', () => {
args.length && window.send('run-command', args[0]);
});
// Called when second instance launched with args (Windows)
app.makeSingleInstance(args => {
args.length && window.send('run-command', args[0]);
});
// Handle URLs when app already open
app.addListener('open-url', (e, url) => {
window.send('run-command', url);
// Apparently a timeout is needed because Chrome steals back focus immediately
// after opening the URL.
setTimeout(() => {
window.focus();
}, 100);
});
});
function initLocalStorage () {
const localStoragePath = path.join(app.getPath('userData'), 'localStorage');
localStorage = new LocalStorage(localStoragePath);
}
function createWindow () {
const zoomFactor = getZoomFactor();
const {bounds, fullscreen} = getBounds();
const {x, y, width, height} = bounds;
// Make sure we don't place the window outside of the visible space
let maxX = 0;
let maxY = 0;
for (const d of electron.screen.getAllDisplays()) {
// Set the maximum placement location to 50 pixels short of the end
maxX = Math.max(maxX, d.bounds.x + d.bounds.width - 50);
maxY = Math.max(maxY, d.bounds.y + d.bounds.height - 50);
}
const finalX = Math.min(maxX, x);
const finalY = Math.min(maxX, y);
mainWindow = new BrowserWindow({
// Make sure we don't initialize the window outside the bounds
x: finalX,
y: finalY,
fullscreen: fullscreen,
fullscreenable: true,
title: appName,
width: width || 1200,
height: height || 600,
minHeight: 500,
minWidth: 500,
acceptFirstMouse: true,
icon: path.resolve(__dirname, 'static/icon.png'),
webPreferences: {
zoomFactor: zoomFactor
}
});
let _resizeTimeout = null;
mainWindow.on('resize', e => {
saveBounds();
clearTimeout(_resizeTimeout);
_resizeTimeout = setTimeout(() => {
trackEvent('Window', 'Resize');
}, 1000);
});
let _moveTimeout = null;
mainWindow.on('move', e => {
saveBounds();
clearTimeout(_moveTimeout);
_moveTimeout = setTimeout(() => {
trackEvent('Window', 'Move');
}, 1000);
});
mainWindow.on('unresponsive', e => {
showUnresponsiveModal();
trackEvent('Window', 'Unresponsive');
});
// and load the app.html of the app.
// TODO: Use path.join for this
mainWindow.loadURL(`file://${__dirname}/renderer.html`);
// Emitted when the window is closed.
mainWindow.on('closed', () => {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
mainWindow = null;
trackEvent('Window', 'Close');
});
require('electron-context-menu')({});
let template = [
{
label: 'Application',
submenu: [
{
label: `About ${appName}`,
role: 'about',
visible: IS_MAC
},
{
type: 'separator',
visible: IS_MAC
},
{
label: 'Preferences',
accelerator: 'CmdOrCtrl+,',
click: function (menuItem, window, e) {
if (!window || !window.webContents) {
return;
}
window.webContents.send('toggle-preferences');
trackEvent('App Menu', 'Preferences');
}
},
{
label: 'Changelog',
click: function (menuItem, window, e) {
if (!window || !window.webContents) {
return;
}
window.webContents.send('toggle-changelog');
trackEvent('App Menu', 'Changelog');
}
},
{
type: 'separator',
visible: IS_MAC
},
{
role: 'hide',
visible: IS_MAC
},
{
role: 'hideothers',
visible: IS_MAC
},
{type: 'separator'},
{
label: 'Quit',
accelerator: 'Command+Q',
click: function () {
app.quit();
}
}
]
},
{
label: 'Edit',
submenu: [{
label: 'Undo',
accelerator: 'CmdOrCtrl+Z',
selector: 'undo:'
}, {
label: 'Redo',
accelerator: 'Shift+CmdOrCtrl+Z',
selector: 'redo:'
}, {
type: 'separator'
}, {
label: 'Cut',
accelerator: 'CmdOrCtrl+X',
selector: 'cut:'
}, {
label: 'Copy',
accelerator: 'CmdOrCtrl+C',
selector: 'copy:'
}, {
label: 'Paste',
accelerator: 'CmdOrCtrl+V',
selector: 'paste:'
}, {
label: 'Select All',
accelerator: 'CmdOrCtrl+A',
selector: 'selectAll:'
}]
},
{
label: 'View',
submenu: [
{
role: 'togglefullscreen'
},
{
label: 'Actual Size',
accelerator: 'CmdOrCtrl+0',
click: () => {
const window = BrowserWindow.getFocusedWindow();
if (!window || !window.webContents) {
return;
}
const zoomFactor = 1;
window.webContents.setZoomFactor(zoomFactor);
saveZoomFactor(zoomFactor);
trackEvent('App Menu', 'Zoom Reset');
}
},
{
label: 'Zoom In',
accelerator: IS_MAC ? 'CmdOrCtrl+Plus' : 'CmdOrCtrl+=',
click: () => {
const window = BrowserWindow.getFocusedWindow();
if (!window || !window.webContents) {
return;
}
const zoomFactor = Math.min(1.8, getZoomFactor() + 0.05);
window.webContents.setZoomFactor(zoomFactor);
saveZoomFactor(zoomFactor);
trackEvent('App Menu', 'Zoom In');
}
},
{
label: 'Zoom Out',
accelerator: 'CmdOrCtrl+-',
click: () => {
const window = BrowserWindow.getFocusedWindow();
if (!window || !window.webContents) {
return;
}
const zoomFactor = Math.max(0.5, getZoomFactor() - 0.05);
window.webContents.setZoomFactor(zoomFactor);
saveZoomFactor(zoomFactor);
trackEvent('App Menu', 'Zoom Out');
}
},
{
label: 'Toggle Sidebar',
accelerator: 'CmdOrCtrl+\\',
click: () => {
const window = BrowserWindow.getFocusedWindow();
if (!window || !window.webContents) {
return;
}
window.webContents.send('toggle-sidebar');
trackEvent('App Menu', 'Toggle Sidebar');
}
}
]
}, {
label: 'Window',
role: 'window',
submenu: [
{role: 'minimize'},
...(IS_MAC ? [{role: 'close'}] : [])
]
}, {
label: 'Help',
role: 'help',
id: 'help',
submenu: [
{
label: 'Contact Support',
click: () => {
trackEvent('App Menu', 'Contact');
shell.openExternal('https://insomnia.rest/documentation/support-and-feedback/');
}
},
{
label: 'Insomnia Help',
accelerator: 'CmdOrCtrl+?',
click: () => {
trackEvent('App Menu', 'Help');
shell.openExternal('https://insomnia.rest/documentation/');
}
}
]
}
];
if (IS_DEV || process.env.INSOMNIA_FORCE_DEBUG) {
template.push({
label: 'Developer',
position: 'before=help',
submenu: [{
label: 'Reload',
accelerator: 'CmdOrCtrl+R',
click: function () {
mainWindow.reload();
}
}, {
label: 'Toggle DevTools',
accelerator: 'Alt+CmdOrCtrl+I',
click: function () {
mainWindow.toggleDevTools();
}
}, {
label: 'Resize to Default',
click: function () {
mainWindow.setBounds({x: 100, y: 100, width: 1000, height: 480});
}
}, {
label: 'Take Screenshot',
click: function () {
mainWindow.capturePage(image => {
const buffer = image.toPNG();
const dir = app.getPath('desktop');
fs.writeFileSync(path.join(dir, `Screenshot-${new Date()}.png`), buffer);
});
}
}]
});
}
Menu.setApplicationMenu(Menu.buildFromTemplate(template));
}

View File

@ -0,0 +1,28 @@
import Raven from 'raven';
import {getAppVersion, isDevelopment} from '../common/constants';
export function init () {
_initSentry();
}
function _initSentry () {
let ravenClient = null;
if (!isDevelopment()) {
ravenClient = Raven.config('https://786e43ae199c4757a9ea4a48a9abd17d@sentry.io/109702', {
environment: isDevelopment() ? 'development' : 'production',
release: getAppVersion(),
logger: 'electron.main'
});
ravenClient.install();
}
process.on('uncaughtException', e => {
if (ravenClient) {
ravenClient.captureException(e, {});
} else {
console.error(e);
}
});
}

77
app/main/updates.js Normal file
View File

@ -0,0 +1,77 @@
import electron from 'electron';
import {CHECK_FOR_UPDATES_INTERVAL, getAppVersion, isDevelopment, isLinux} from '../common/constants';
const {autoUpdater, BrowserWindow} = electron;
const UPDATE_URLS = {
darwin: `https://updates.insomnia.rest/builds/check/mac?v=${getAppVersion()}`,
linux: `https://updates.insomnia.rest/builds/check/linux?v=${getAppVersion()}`,
win32: `https://downloads.insomnia.rest/win`
};
let hasPromptedForUpdates = false;
export function init () {
// Check for updates immediately
_checkForUpdates();
// Check for updates on an interval
setInterval(_checkForUpdates, CHECK_FOR_UPDATES_INTERVAL);
autoUpdater.on('error', e => {
// NOTE: Don't report autoUpdater errors to Sentry. They are way too noisy.
if (isDevelopment()) {
console.error(e);
}
});
autoUpdater.on('update-not-available', () => {
console.log('-- Update Not Available --');
});
autoUpdater.on('update-available', () => {
console.log('-- Update Available --');
});
autoUpdater.on('update-downloaded', (e, releaseNotes, releaseName, releaseDate, updateUrl) => {
console.log(`-- Update Downloaded ${releaseName} --`);
_showUpdateNotification();
});
}
function _showUpdateNotification () {
if (hasPromptedForUpdates) {
return;
}
const window = BrowserWindow.getFocusedWindow();
if (!window || !window.webContents) {
return;
}
window.webContents.send('update-available');
hasPromptedForUpdates = true;
}
function _checkForUpdates () {
if (hasPromptedForUpdates) {
// We've already prompted for updates. Don't bug the user anymore
return;
}
if (isDevelopment()) {
console.log('-- Skipping update check in Development --');
return;
} else {
console.log('-- Checking for Updates --');
}
if (!isLinux()) {
try {
autoUpdater.setFeedURL(UPDATE_URLS[process.platform]);
autoUpdater.checkForUpdates();
} catch (e) {
// This will fail in development
}
}
}

354
app/main/window-utils.js Normal file
View File

@ -0,0 +1,354 @@
import electron from 'electron';
import path from 'path';
import fs from 'fs';
import LocalStorage from '../common/local-storage';
import {getAppName, isDevelopment, isMac} from '../common/constants';
const {app, Menu, BrowserWindow, shell, dialog} = electron;
let mainWindow = null;
let localStorage = null;
export function createWindow () {
const zoomFactor = getZoomFactor();
const {bounds, fullscreen} = getBounds();
const {x, y, width, height} = bounds;
// Make sure we don't place the window outside of the visible space
let maxX = 0;
let maxY = 0;
for (const d of electron.screen.getAllDisplays()) {
// Set the maximum placement location to 50 pixels short of the end
maxX = Math.max(maxX, d.bounds.x + d.bounds.width - 50);
maxY = Math.max(maxY, d.bounds.y + d.bounds.height - 50);
}
const finalX = Math.min(maxX, x);
const finalY = Math.min(maxX, y);
mainWindow = new BrowserWindow({
// Make sure we don't initialize the window outside the bounds
x: finalX,
y: finalY,
fullscreen: fullscreen,
fullscreenable: true,
title: getAppName(),
width: width || 1200,
height: height || 600,
minHeight: 500,
minWidth: 500,
acceptFirstMouse: true,
icon: path.resolve(__dirname, 'static/icon.png'),
webPreferences: {
zoomFactor: zoomFactor
}
});
let _resizeTimeout = null;
mainWindow.on('resize', e => {
saveBounds();
clearTimeout(_resizeTimeout);
_resizeTimeout = setTimeout(() => {
trackEvent('Window', 'Resize');
}, 1000);
});
let _moveTimeout = null;
mainWindow.on('move', e => {
saveBounds();
clearTimeout(_moveTimeout);
_moveTimeout = setTimeout(() => {
trackEvent('Window', 'Move');
}, 1000);
});
mainWindow.on('unresponsive', e => {
showUnresponsiveModal();
trackEvent('Window', 'Unresponsive');
});
// and load the app.html of the app.
// TODO: Use path.join for this
mainWindow.loadURL(`file://${__dirname}/renderer.html`);
// Emitted when the window is closed.
mainWindow.on('closed', () => {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
mainWindow = null;
trackEvent('Window', 'Close');
});
const applicationMenu = {
label: 'Application',
submenu: [
...(isMac() ? [
{label: `About ${getAppName()}`, role: 'about'},
{type: 'separator'}
] : []),
{
label: 'Preferences',
accelerator: 'CmdOrCtrl+,',
click: function (menuItem, window, e) {
if (!window || !window.webContents) {
return;
}
window.webContents.send('toggle-preferences');
trackEvent('App Menu', 'Preferences');
}
},
{
label: 'Changelog',
click: function (menuItem, window, e) {
if (!window || !window.webContents) {
return;
}
window.webContents.send('toggle-changelog');
trackEvent('App Menu', 'Changelog');
}
},
...(isMac() ? [
{type: 'separator'},
{role: 'hide'},
{role: 'hideothers'}
] : []),
{type: 'separator'},
{label: 'Quit', accelerator: 'Command+Q', click: () => app.quit()}
]
};
const editMenu = {
label: 'Edit',
submenu: [
{label: 'Undo', accelerator: 'CmdOrCtrl+Z', selector: 'undo:'},
{label: 'Redo', accelerator: 'Shift+CmdOrCtrl+Z', selector: 'redo:'},
{type: 'separator'},
{label: 'Cut', accelerator: 'CmdOrCtrl+X', selector: 'cut:'},
{label: 'Copy', accelerator: 'CmdOrCtrl+C', selector: 'copy:'},
{label: 'Paste', accelerator: 'CmdOrCtrl+V', selector: 'paste:'},
{label: 'Select All', accelerator: 'CmdOrCtrl+A', selector: 'selectAll:'}
]
};
const viewMenu = {
label: 'View',
submenu: [
{role: 'togglefullscreen'},
{
label: 'Actual Size',
accelerator: 'CmdOrCtrl+0',
click: () => {
const window = BrowserWindow.getFocusedWindow();
if (!window || !window.webContents) {
return;
}
const zoomFactor = 1;
window.webContents.setZoomFactor(zoomFactor);
saveZoomFactor(zoomFactor);
trackEvent('App Menu', 'Zoom Reset');
}
},
{
label: 'Zoom In',
accelerator: isMac() ? 'CmdOrCtrl+Plus' : 'CmdOrCtrl+=',
click: () => {
const window = BrowserWindow.getFocusedWindow();
if (!window || !window.webContents) {
return;
}
const zoomFactor = Math.min(1.8, getZoomFactor() + 0.05);
window.webContents.setZoomFactor(zoomFactor);
saveZoomFactor(zoomFactor);
trackEvent('App Menu', 'Zoom In');
}
},
{
label: 'Zoom Out',
accelerator: 'CmdOrCtrl+-',
click: () => {
const window = BrowserWindow.getFocusedWindow();
if (!window || !window.webContents) {
return;
}
const zoomFactor = Math.max(0.5, getZoomFactor() - 0.05);
window.webContents.setZoomFactor(zoomFactor);
saveZoomFactor(zoomFactor);
trackEvent('App Menu', 'Zoom Out');
}
},
{
label: 'Toggle Sidebar',
accelerator: 'CmdOrCtrl+\\',
click: () => {
const window = BrowserWindow.getFocusedWindow();
if (!window || !window.webContents) {
return;
}
window.webContents.send('toggle-sidebar');
trackEvent('App Menu', 'Toggle Sidebar');
}
}
]
};
const windowMenu = {
label: 'Window',
role: 'window',
submenu: [
{role: 'minimize'},
...(isMac() ? [{role: 'close'}] : [])
]
};
const helpMenu = {
label: 'Help',
role: 'help',
id: 'help',
submenu: [
{
label: 'Contact Support',
click: () => {
trackEvent('App Menu', 'Contact');
shell.openExternal('https://insomnia.rest/documentation/support-and-feedback/');
}
},
{
label: 'Insomnia Help',
accelerator: 'CmdOrCtrl+?',
click: () => {
trackEvent('App Menu', 'Help');
shell.openExternal('https://insomnia.rest/documentation/');
}
}
]
};
const developerMenu = {
label: 'Developer',
position: 'before=help',
submenu: [{
label: 'Reload',
accelerator: 'CmdOrCtrl+R',
click: () => mainWindow.reload()
}, {
label: 'Toggle DevTools',
accelerator: 'Alt+CmdOrCtrl+I',
click: () => mainWindow.toggleDevTools()
}, {
label: 'Resize to Default',
click: () => mainWindow.setBounds({x: 100, y: 100, width: 1000, height: 480})
}, {
label: 'Take Screenshot',
click: function () {
mainWindow.capturePage(image => {
const buffer = image.toPNG();
const dir = app.getPath('desktop');
fs.writeFileSync(path.join(dir, `Screenshot-${new Date()}.png`), buffer);
});
}
}]
};
let template = [];
template.push(applicationMenu);
template.push(editMenu);
template.push(viewMenu);
template.push(windowMenu);
template.push(helpMenu);
if (isDevelopment() || process.env.INSOMNIA_FORCE_DEBUG) {
template.push(developerMenu);
}
Menu.setApplicationMenu(Menu.buildFromTemplate(template));
require('electron-context-menu')({});
return mainWindow;
}
function showUnresponsiveModal () {
dialog.showMessageBox({
type: 'info',
buttons: ['Cancel', 'Reload'],
defaultId: 1,
cancelId: 0,
title: 'Unresponsive',
message: 'Insomnia has become unresponsive. Do you want to reload?'
}, id => {
if (id === 1) {
mainWindow.destroy();
createWindow();
}
});
}
function trackEvent (...args) {
const window = BrowserWindow.getFocusedWindow();
if (!window || !window.webContents) {
return;
}
window.webContents.send('analytics-track-event', args);
}
function saveBounds () {
if (!mainWindow) {
return;
}
const fullscreen = mainWindow.isFullScreen();
// Only save the size if we're not in fullscreen
if (!fullscreen) {
localStorage.setItem('bounds', mainWindow.getBounds());
localStorage.setItem('fullscreen', false);
} else {
localStorage.setItem('fullscreen', true);
}
}
function getBounds () {
let bounds = {};
let fullscreen = false;
try {
bounds = localStorage.getItem('bounds', {});
fullscreen = localStorage.getItem('fullscreen', false);
} catch (e) {
// This should never happen, but if it does...!
console.error('Failed to parse window bounds', e);
}
return {bounds, fullscreen};
}
function saveZoomFactor (zoomFactor) {
localStorage.setItem('zoomFactor', zoomFactor);
}
function getZoomFactor () {
let zoomFactor = 1;
try {
zoomFactor = localStorage.getItem('zoomFactor', 1);
} catch (e) {
// This should never happen, but if it does...!
console.error('Failed to parse zoomFactor', e);
}
return zoomFactor;
}
function initLocalStorage () {
const localStoragePath = path.join(app.getPath('userData'), 'localStorage');
localStorage = new LocalStorage(localStoragePath);
}
initLocalStorage();

View File

@ -1,28 +1,27 @@
{
"private": true,
"name": "insomnia",
"version": "5.0.10",
"version": "5.0.12",
"productName": "Insomnia",
"longName": "Insomnia REST Client",
"description": "Debug APIs like a human, not a robot",
"homepage": "https://insomnia.rest",
"author": "Insomnia <support@insomnia.rest>",
"main": "main.js",
"main": "main.min.js",
"dependencies": {
"electron-context-menu": "~0.8.0",
"electron-squirrel-startup": "~1.0.0",
"electron-context-menu": "0.9.0",
"electron-squirrel-startup": "1.0.0",
"hkdf": "0.0.2",
"httpsnippet": "1.16.5",
"insomnia-importers": "~1.3.1",
"jsonpath": "~0.2.9",
"mkdirp": "~0.5.1",
"nedb": "~1.8.0",
"node-forge": "~0.7.0",
"insomnia-importers": "1.3.3",
"jsonpath": "0.2.11",
"mkdirp": "0.5.1",
"nedb": "1.8.0",
"node-forge": "0.7.0",
"node-libcurl": "git://github.com/getinsomnia/node-libcurl.git#3fc2afea435f3548eedbef9c68d3fee642dfcddb",
"raven": "~1.1.2",
"request": "~2.71.0",
"raven": "1.1.2",
"srp-js": "0.2.0",
"tough-cookie": "~2.3.1",
"vkbeautify": "~0.99.1"
"tough-cookie": "2.3.1",
"vkbeautify": "0.99.1"
}
}

View File

@ -18,8 +18,6 @@
<script>
// UPDATE HANDLERS
(function () {
const CHECK_FOR_UPDATES_INTERVAL = 1000 * 60 * 60 * 3; // 3 hours
function showUpdateNotification () {
console.log('-- Update Available --');
@ -37,12 +35,6 @@
// you relaunch too soon it doesn't work the first time.
setTimeout(showUpdateNotification, 1000 * 10);
});
function checkForUpdates () {
ipcRenderer.send('check-for-updates');
}
setInterval(checkForUpdates, CHECK_FOR_UPDATES_INTERVAL);
})();
</script>
<script>

View File

@ -450,8 +450,8 @@ export async function createOrUpdateConfig (resourceGroupId, syncMode) {
}
export async function logout () {
await resetLocalData();
await session.logout();
await resetLocalData();
}
export async function cancelTrial () {

View File

@ -360,12 +360,17 @@ class CodeEditor extends PureComponent {
lineNumbers: !hideGutters && !hideLineNumbers,
foldGutter: !hideGutters && !hideLineNumbers,
lineWrapping: lineWrapping,
keyMap: keyMap || 'default',
matchBrackets: !noMatchBrackets,
lint: !noLint && !readOnly,
gutters: []
};
// Only set keyMap if we're not read-only. This is so things like
// ctrl-a work on read-only mode.
if (!readOnly && keyMap) {
options.keyMap = keyMap;
}
if (indentSize) {
options.tabSize = indentSize;
options.indentUnit = indentSize;

View File

@ -53,11 +53,16 @@ class SettingsModal extends PureComponent {
this.modal.hide();
}
_handleImport () {
_handleImportFile () {
this.props.handleImportFile();
this.modal.hide();
}
_handleImportUri (uri) {
this.props.handleImportUri(uri);
this.modal.hide();
}
_handleChangeTheme (theme, persist = true) {
document.body.setAttribute('theme', theme);
@ -148,7 +153,8 @@ class SettingsModal extends PureComponent {
<ImportExport
handleExportAll={this._handleExportAllToFile}
handleExportWorkspace={this._handleExportWorkspace}
handleImport={this._handleImport}
handleImportFile={this._handleImportFile}
handleImportUri={this._handleImportUri}
/>
</TabPanel>
<TabPanel className="scrollable">
@ -178,6 +184,7 @@ SettingsModal.propTypes = {
handleExportWorkspaceToFile: PropTypes.func.isRequired,
handleExportAllToFile: PropTypes.func.isRequired,
handleImportFile: PropTypes.func.isRequired,
handleImportUri: PropTypes.func.isRequired,
// Properties
settings: PropTypes.object.isRequired

View File

@ -1,13 +1,25 @@
import React, {PureComponent, PropTypes} from 'react';
import autobind from 'autobind-decorator';
import {Dropdown, DropdownButton, DropdownItem, DropdownDivider} from '../base/dropdown';
import PromptModal from '../modals/prompt-modal';
import Link from '../base/link';
import {showModal} from '../modals/index';
@autobind
class ImportExport extends PureComponent {
async _handleImportUri () {
const uri = await showModal(PromptModal, {
headerName: 'Import Data from URL',
submitName: 'Fetch and Import',
label: 'URL',
placeholder: 'https://website.com/insomnia-import.json'
});
this.props.handleImportUri(uri);
}
render () {
const {
handleImport,
handleImportFile,
handleExportAll,
handleExportWorkspace
} = this.props;
@ -15,7 +27,8 @@ class ImportExport extends PureComponent {
return (
<div>
<p className="no-margin-top">
Import format will be automatically detected (<strong>Insomnia, Postman v2, HAR, Curl</strong>)
Import format will be automatically detected (<strong>Insomnia, Postman v2, HAR,
Curl</strong>)
</p>
<p>
Don't see your format here?
@ -29,18 +42,29 @@ class ImportExport extends PureComponent {
</DropdownButton>
<DropdownDivider>Choose Export Type</DropdownDivider>
<DropdownItem onClick={handleExportWorkspace}>
<i className="fa fa-home"></i>
<i className="fa fa-home"/>
Current Workspace
</DropdownItem>
<DropdownItem onClick={handleExportAll}>
<i className="fa fa-empty"></i>
<i className="fa fa-empty"/>
All Workspaces
</DropdownItem>
</Dropdown>
&nbsp;&nbsp;
<button className="btn btn--clicky" onClick={handleImport}>
Import Data
</button>
<Dropdown outline>
<DropdownButton className="btn btn--clicky">
Import Data <i className="fa fa-caret-down"></i>
</DropdownButton>
<DropdownDivider>Choose Import Type</DropdownDivider>
<DropdownItem onClick={handleImportFile}>
<i className="fa fa-file-o"/>
From File
</DropdownItem>
<DropdownItem onClick={this._handleImportUri}>
<i className="fa fa-link"/>
From URL
</DropdownItem>
</Dropdown>
</div>
<p className="italic faint">
* Tip: You can also paste Curl commands into the URL bar
@ -51,7 +75,8 @@ class ImportExport extends PureComponent {
}
ImportExport.propTypes = {
handleImport: PropTypes.func.isRequired,
handleImportFile: PropTypes.func.isRequired,
handleImportUri: PropTypes.func.isRequired,
handleExportAll: PropTypes.func.isRequired,
handleExportWorkspace: PropTypes.func.isRequired
};

View File

@ -132,6 +132,10 @@ class Wrapper extends PureComponent {
this.props.handleImportFileToWorkspace(this.props.activeWorkspace._id);
}
_handleImportUri (uri) {
this.props.handleImportUriToWorkspace(this.props.activeWorkspace._id, uri);
}
_handleExportWorkspaceToFile () {
this.props.handleExportFile(this.props.activeWorkspace._id);
}
@ -401,6 +405,7 @@ class Wrapper extends PureComponent {
handleExportWorkspaceToFile={this._handleExportWorkspaceToFile}
handleExportAllToFile={handleExportFile}
handleImportFile={this._handleImportFile}
handleImportUri={this._handleImportUri}
settings={settings}
/>
<RequestSwitcherModal
@ -447,6 +452,7 @@ Wrapper.propTypes = {
handleActivateRequest: PropTypes.func.isRequired,
handleSetSidebarFilter: PropTypes.func.isRequired,
handleImportFileToWorkspace: PropTypes.func.isRequired,
handleImportUriToWorkspace: PropTypes.func.isRequired,
handleExportFile: PropTypes.func.isRequired,
handleSetActiveWorkspace: PropTypes.func.isRequired,
handleSetActiveEnvironment: PropTypes.func.isRequired,

View File

@ -1,6 +1,7 @@
import React, {PropTypes, PureComponent} from 'react';
import autobind from 'autobind-decorator';
import fs from 'fs';
import {parse as urlParse} from 'url';
import {ipcRenderer} from 'electron';
import ReactDOM from 'react-dom';
import {connect} from 'react-redux';
@ -662,11 +663,23 @@ class App extends PureComponent {
showModal(SettingsModal);
});
ipcRenderer.on('run-command', (e, commandUri) => {
const parsed = urlParse(commandUri, true);
const command = `${parsed.hostname}${parsed.pathname}`;
const args = JSON.parse(JSON.stringify(parsed.query));
args.workspaceId = args.workspaceId || this.props.activeWorkspace._id;
this.props.handleCommand(command, args);
});
ipcRenderer.on('toggle-changelog', () => {
showModal(ChangelogModal);
});
ipcRenderer.on('toggle-sidebar', this._handleToggleSidebar);
process.nextTick(() => ipcRenderer.send('app-ready'));
}
componentWillUnmount () {
@ -728,6 +741,7 @@ App.propTypes = {
sidebarWidth: PropTypes.number.isRequired,
paneWidth: PropTypes.number.isRequired,
paneHeight: PropTypes.number.isRequired,
handleCommand: PropTypes.func.isRequired,
activeWorkspace: PropTypes.shape({
_id: PropTypes.string.isRequired
}).isRequired,
@ -823,6 +837,8 @@ function mapDispatchToProps (dispatch) {
handleSetActiveWorkspace: global.setActiveWorkspace,
handleImportFileToWorkspace: global.importFile,
handleImportUriToWorkspace: global.importUri,
handleCommand: global.newCommand,
handleExportFile: global.exportFile,
handleMoveRequest: _moveRequest,
handleMoveRequestGroup: _moveRequestGroup

View File

@ -118,6 +118,7 @@
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 1px;
}
.modal__footer > *:last-child {

View File

@ -169,7 +169,7 @@
.sidebar__list-root {
// Add some space above so it's not so squished
border-top: 1px solid @hl-sm;
padding-top: @padding-xs;
padding-top: @padding-xxs;
padding-bottom: @padding-md;
box-shadow: inset 0 2rem 2rem -2rem rgba(0, 0, 0, 0.03);

View File

@ -1,8 +1,9 @@
import electron from 'electron';
import React from 'react';
import {combineReducers} from 'redux';
import fs from 'fs';
import {importRaw, exportJSON} from '../../../common/import';
import * as importUtils from '../../../common/import';
import {trackEvent} from '../../../analytics';
import AlertModal from '../../components/modals/alert-modal';
import {showModal} from '../../components/modals';
@ -21,6 +22,7 @@ const SET_ACTIVE_WORKSPACE = 'global/activate-workspace';
const COMMAND_ALERT = 'app/alert';
const COMMAND_LOGIN = 'app/auth/login';
const COMMAND_TRIAL_END = 'app/billing/trial-end';
const COMMAND_IMPORT_URI = 'app/import';
// ~~~~~~~~ //
// REDUCERS //
@ -68,18 +70,25 @@ export const reducer = combineReducers({
// ~~~~~~~ //
export function newCommand (command, args) {
// TODO: Make this use reducer when Modals ported to Redux
if (command === COMMAND_ALERT) {
const {message, title} = args;
showModal(AlertModal, {title, message});
} else if (command === COMMAND_LOGIN) {
const {title, message} = args;
showModal(LoginModal, {title, message});
} else if (command === COMMAND_TRIAL_END) {
showModal(PaymentNotificationModal);
}
return {type: command, ...args};
return async dispatch => {
// TODO: Make this use reducer when Modals ported to Redux
if (command === COMMAND_ALERT) {
const {message, title} = args;
showModal(AlertModal, {title, message});
} else if (command === COMMAND_LOGIN) {
const {title, message} = args;
showModal(LoginModal, {title, message});
} else if (command === COMMAND_TRIAL_END) {
showModal(PaymentNotificationModal);
} else if (command === COMMAND_IMPORT_URI) {
await showModal(AlertModal, {
title: 'Confirm Data Import',
message: <span>Do you really want to import <code>{args.uri}</code>?</span>,
addCancel: true
});
dispatch(importUri(args.workspaceId, args.uri));
}
};
}
export function loadStart () {
@ -114,7 +123,6 @@ export function importFile (workspaceId) {
return async dispatch => {
dispatch(loadStart());
const workspace = await models.workspace.getById(workspaceId);
const options = {
title: 'Import Insomnia Data',
buttonLabel: 'Import',
@ -132,53 +140,48 @@ export function importFile (workspaceId) {
if (!paths) {
// It was cancelled, so let's bail out
dispatch(loadStop());
trackEvent('Import', 'Cancel');
trackEvent('Import File', 'Cancel');
return;
}
// Let's import all the paths!
for (const path of paths) {
try {
const data = fs.readFileSync(path, 'utf8');
const uri = `file://${path}`;
await importUtils.importUri(workspaceId, uri);
trackEvent('Import File', 'Success');
} catch (err) {
showModal(AlertModal, {title: 'Import Failed', message: err + ''});
trackEvent('Import File', 'Failure');
} finally {
dispatch(loadStop());
const result = await importRaw(workspace, data);
const {summary, source, error} = result;
if (error) {
showModal(AlertModal, {title: 'Import Failed', message: error});
return;
}
let statements = Object.keys(summary).map(type => {
const count = summary[type].length;
const name = models.getModelName(type, count);
return count === 0 ? null : `${count} ${name}`;
}).filter(s => s !== null);
let message;
if (statements.length === 0) {
message = 'Nothing was found to import.';
} else {
message = `You imported ${statements.join(', ')}!`;
}
showModal(AlertModal, {title: 'Import Succeeded', message});
trackEvent('Import', 'Success', source);
} catch (e) {
showModal(AlertModal, {title: 'Import Failed', message: e + ''});
trackEvent('Import', 'Failure');
}
}
});
};
}
export function importUri (workspaceId, uri) {
return async dispatch => {
dispatch(loadStart());
try {
await importUtils.importUri(workspaceId, uri);
trackEvent('Import URI', 'Success');
} catch (err) {
trackEvent('Import URI', 'Failure');
showModal(AlertModal, {title: 'Import Failed', message: err + ''});
} finally {
dispatch(loadStop());
}
};
}
export function exportFile (workspaceId = null) {
return async dispatch => {
dispatch(loadStart());
const workspace = await models.workspace.getById(workspaceId);
const json = await exportJSON(workspace);
const json = await importUtils.exportJSON(workspace);
const options = {
title: 'Export Insomnia Data',
buttonLabel: 'Export',

View File

@ -50,6 +50,5 @@ test_script:
# global handlers #
#---------------------------------#
#deploy_script:
# - npm run build-n-package:win
# - ps: ls .\dist\win\* | % { Push-AppveyorArtifact $_.FullName }
deploy_script:
- ps: If ($env:APPVEYOR_REPO_TAG -Match "true") {npm run build-n-package:win ; ls .\dist\win\* | % { Push-AppveyorArtifact $_.FullName }}

View File

@ -16,10 +16,10 @@
"test:coverage": "cross-env NODE_ENV=test jest --coverage --silent && open ./coverage/lcov-report/index.html",
"test:watch": "cross-env NODE_ENV=test jest --silent --watch",
"test": "npm run test:lint && cross-env NODE_ENV=test jest --silent",
"start-hot": "npm run build-main && cross-env HOT=1 INSOMNIA_ENV=development electron -r babel-register ./app/main.min.js",
"start-hot": "npm run build-main && cross-env HOT=1 INSOMNIA_ENV=development electron app",
"build-main": "cross-env NODE_ENV=development webpack --config ./webpack/webpack.config.electron.babel.js",
"hot-server": "webpack-dev-server --config ./webpack/webpack.config.development.babel.js",
"dev": "concurrently --kill-others \"npm run hot-server\" \"npm run start-hot\"",
"dev": "concurrently --kill-others 'npm run hot-server' 'npm run start-hot'",
"build:clean": "rm -rf ./build/* && rm -rf ./dist/* && mkdirp ./build",
"build:renderer": "cross-env NODE_ENV=production webpack --config ./webpack/webpack.config.production.babel.js",
"build:main": "cross-env NODE_ENV=production webpack --config ./webpack/webpack.config.electron.babel.js",
@ -67,6 +67,14 @@
"build": {
"appId": "com.insomnia.app",
"category": "public.app-category.developer-tools",
"protocols": [
{
"name": "Insomnia",
"role": "Viewer",
"schemes": ["insomnia"]
}
],
"fileAssociations": [],
"directories": {
"app": "build",
"output": "dist"
@ -99,88 +107,87 @@
}
},
"dependencies": {
"autobind-decorator": "^1.3.4",
"classnames": "~2.2.5",
"clone": "~2.1.0",
"codemirror": "~5.24.2",
"electron-context-menu": "~0.8.0",
"electron-squirrel-startup": "~1.0.0",
"autobind-decorator": "1.3.4",
"classnames": "2.2.5",
"clone": "2.1.0",
"codemirror": "5.24.2",
"electron-context-menu": "0.9.0",
"electron-squirrel-startup": "1.0.0",
"hkdf": "0.0.2",
"html-entities": "^1.2.0",
"html-entities": "1.2.0",
"httpsnippet": "1.16.5",
"iconv-lite": "^0.4.15",
"insomnia-importers": "~1.3.1",
"jsonlint": "~1.6.2",
"jsonpath": "~0.2.11",
"mime-types": "~2.1.14",
"mkdirp": "~0.5.1",
"moment": "^2.18.1",
"nedb": "~1.8.0",
"node-forge": "~0.7.0",
"iconv-lite": "0.4.15",
"insomnia-importers": "1.3.3",
"jsonlint": "1.6.2",
"jsonpath": "0.2.11",
"mime-types": "2.1.14",
"mkdirp": "0.5.1",
"moment": "2.18.1",
"nedb": "1.8.0",
"node-forge": "0.7.0",
"node-libcurl": "git://github.com/getinsomnia/node-libcurl.git#3fc2afea435f3548eedbef9c68d3fee642dfcddb",
"nunjucks": "~3.0.0",
"raven": "~1.1.2",
"react": "~15.4.2",
"react-dnd": "~2.2.3",
"react-dnd-html5-backend": "~2.2.3",
"react-dom": "~15.4.2",
"react-redux": "~5.0.3",
"react-tabs": "~0.8.2",
"redux": "~3.3.1",
"redux-thunk": "~2.0.1",
"request": "~2.74.0",
"reselect": "~2.5.4",
"nunjucks": "3.0.0",
"raven": "1.1.2",
"react": "15.4.2",
"react-dnd": "2.2.3",
"react-dnd-html5-backend": "2.2.3",
"react-dom": "15.4.2",
"react-redux": "5.0.3",
"react-tabs": "0.8.2",
"redux": "3.3.1",
"redux-thunk": "2.0.1",
"reselect": "2.5.4",
"srp-js": "0.2.0",
"tough-cookie": "~2.3.1",
"uuid": "~3.0.0",
"vkbeautify": "~0.99.1",
"whatwg-fetch": "~2.0.1",
"xmldom": "~0.1.22",
"xpath": "~0.0.23"
"tough-cookie": "2.3.1",
"uuid": "3.0.0",
"vkbeautify": "0.99.1",
"whatwg-fetch": "2.0.1",
"xmldom": "0.1.22",
"xpath": "0.0.23"
},
"devDependencies": {
"babel-cli": "^6.23.0",
"babel-core": "~6.23.1",
"babel-eslint": "^7.1.1",
"babel-jest": "~19.0.0",
"babel-loader": "~6.3.2",
"babel-plugin-transform-decorators-legacy": "^1.3.4",
"babel-plugin-transform-object-rest-spread": "~6.23.0",
"babel-plugin-transform-regenerator": "~6.22.0",
"babel-plugin-transform-runtime": "~6.15.0",
"babel-polyfill": "~6.23.0",
"babel-preset-es2015": "^6.22.0",
"babel-preset-react": "~6.23.0",
"babel-preset-react-hmre": "~1.1.1",
"concurrently": "~2.0.0",
"cross-env": "~2.0.0",
"css-loader": "~0.26.2",
"electron": "~1.6.5",
"electron-builder": "~10.17.3",
"electron-rebuild": "^1.5.7",
"eslint": "^3.16.1",
"eslint-config-semistandard": "^7.0.0",
"eslint-config-standard": "^7.0.0",
"eslint-plugin-filenames": "^1.1.0",
"eslint-plugin-html": "^2.0.1",
"eslint-plugin-jest": "^19.0.1",
"eslint-plugin-json": "^1.2.0",
"eslint-plugin-promise": "^3.5.0",
"eslint-plugin-react": "^6.10.0",
"eslint-plugin-standard": "^2.1.1",
"file-loader": "~0.10.1",
"jest": "~19.0.2",
"less": "~2.7.2",
"less-loader": "~2.2.3",
"react-hot-loader": "~3.0.0-beta.6",
"redux-mock-store": "~1.0.2",
"style-loader": "~0.13.2",
"url-loader": "^0.5.8",
"webpack": "~2.2.1",
"webpack-dev-middleware": "~1.10.1",
"webpack-dev-server": "^2.4.1",
"webpack-hot-middleware": "~2.17.1",
"webpack-target-electron-renderer": "~0.4.0"
"babel-cli": "6.23.0",
"babel-core": "6.23.1",
"babel-eslint": "7.1.1",
"babel-jest": "19.0.0",
"babel-loader": "6.3.2",
"babel-plugin-transform-decorators-legacy": "1.3.4",
"babel-plugin-transform-object-rest-spread": "6.23.0",
"babel-plugin-transform-regenerator": "6.22.0",
"babel-plugin-transform-runtime": "6.15.0",
"babel-polyfill": "6.23.0",
"babel-preset-es2015": "6.22.0",
"babel-preset-react": "6.23.0",
"babel-preset-react-hmre": "1.1.1",
"concurrently": "2.0.0",
"cross-env": "2.0.0",
"css-loader": "0.26.2",
"electron": "1.6.6",
"electron-builder": "10.17.3",
"electron-rebuild": "1.5.7",
"eslint": "3.16.1",
"eslint-config-semistandard": "7.0.0",
"eslint-config-standard": "7.0.0",
"eslint-plugin-filenames": "1.1.0",
"eslint-plugin-html": "2.0.1",
"eslint-plugin-jest": "19.0.1",
"eslint-plugin-json": "1.2.0",
"eslint-plugin-promise": "3.5.0",
"eslint-plugin-react": "6.10.0",
"eslint-plugin-standard": "2.1.1",
"file-loader": "0.10.1",
"jest": "19.0.2",
"less": "2.7.2",
"less-loader": "2.2.3",
"react-hot-loader": "3.0.0-beta.6",
"redux-mock-store": "1.0.2",
"style-loader": "0.13.2",
"url-loader": "0.5.8",
"webpack": "2.2.1",
"webpack-dev-middleware": "1.10.1",
"webpack-dev-server": "2.4.1",
"webpack-hot-middleware": "2.17.1",
"webpack-target-electron-renderer": "0.4.0"
},
"devEngines": {
"node": "6.x",

View File

@ -1,6 +0,0 @@
#!/bin/bash
APP_VERSION=$(node -e "console.log(require('./build/package.json').version)")
HASH=$(md5 ./dist/insomnia-*.deb)
echo "$HASH for $APP_VERSION"

View File

@ -1,19 +1,27 @@
const webpack = require('webpack');
const path = require('path');
const productionConfig = require('./webpack.config.production.babel');
let devtool;
let plugins;
const output = {
libraryTarget: 'commonjs2'
libraryTarget: 'commonjs2',
filename: 'main.min.js'
};
if (process.env.NODE_ENV === 'development') {
output.path = path.join(__dirname, '../app');
output.filename = 'main.min.js';
devtool = 'eval-source-map';
plugins = [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('development'),
'process.env.INSOMNIA_ENV': JSON.stringify('development')
})
]
} else {
output.path = path.join(__dirname, '../build');
output.filename = 'main.js';
devtool = productionConfig.devtool;
plugins = productionConfig.plugins;
}
module.exports = {
@ -27,5 +35,6 @@ module.exports = {
__dirname: false,
__filename: false
},
target: 'electron-main'
target: 'electron-main',
plugins: plugins
};