mirror of
https://github.com/laurent22/joplin.git
synced 2025-03-11 14:09:55 +02:00
This commit is contained in:
parent
86b00d0a2b
commit
694ca6480e
@ -415,6 +415,8 @@ packages/app-desktop/utils/checkForUpdatesUtils.test.js
|
|||||||
packages/app-desktop/utils/checkForUpdatesUtils.js
|
packages/app-desktop/utils/checkForUpdatesUtils.js
|
||||||
packages/app-desktop/utils/checkForUpdatesUtilsTestData.js
|
packages/app-desktop/utils/checkForUpdatesUtilsTestData.js
|
||||||
packages/app-desktop/utils/markupLanguageUtils.js
|
packages/app-desktop/utils/markupLanguageUtils.js
|
||||||
|
packages/app-desktop/utils/restartInSafeModeFromMain.test.js
|
||||||
|
packages/app-desktop/utils/restartInSafeModeFromMain.js
|
||||||
packages/app-mobile/PluginAssetsLoader.js
|
packages/app-mobile/PluginAssetsLoader.js
|
||||||
packages/app-mobile/components/ActionButton.js
|
packages/app-mobile/components/ActionButton.js
|
||||||
packages/app-mobile/components/BackButtonDialogBox.js
|
packages/app-mobile/components/BackButtonDialogBox.js
|
||||||
@ -902,6 +904,7 @@ packages/lib/themes/type.js
|
|||||||
packages/lib/time.js
|
packages/lib/time.js
|
||||||
packages/lib/utils/credentialFiles.js
|
packages/lib/utils/credentialFiles.js
|
||||||
packages/lib/utils/joplinCloud.js
|
packages/lib/utils/joplinCloud.js
|
||||||
|
packages/lib/utils/processStartFlags.js
|
||||||
packages/lib/utils/userFetcher.js
|
packages/lib/utils/userFetcher.js
|
||||||
packages/lib/utils/webDAVUtils.test.js
|
packages/lib/utils/webDAVUtils.test.js
|
||||||
packages/lib/utils/webDAVUtils.js
|
packages/lib/utils/webDAVUtils.js
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -397,6 +397,8 @@ packages/app-desktop/utils/checkForUpdatesUtils.test.js
|
|||||||
packages/app-desktop/utils/checkForUpdatesUtils.js
|
packages/app-desktop/utils/checkForUpdatesUtils.js
|
||||||
packages/app-desktop/utils/checkForUpdatesUtilsTestData.js
|
packages/app-desktop/utils/checkForUpdatesUtilsTestData.js
|
||||||
packages/app-desktop/utils/markupLanguageUtils.js
|
packages/app-desktop/utils/markupLanguageUtils.js
|
||||||
|
packages/app-desktop/utils/restartInSafeModeFromMain.test.js
|
||||||
|
packages/app-desktop/utils/restartInSafeModeFromMain.js
|
||||||
packages/app-mobile/PluginAssetsLoader.js
|
packages/app-mobile/PluginAssetsLoader.js
|
||||||
packages/app-mobile/components/ActionButton.js
|
packages/app-mobile/components/ActionButton.js
|
||||||
packages/app-mobile/components/BackButtonDialogBox.js
|
packages/app-mobile/components/BackButtonDialogBox.js
|
||||||
@ -884,6 +886,7 @@ packages/lib/themes/type.js
|
|||||||
packages/lib/time.js
|
packages/lib/time.js
|
||||||
packages/lib/utils/credentialFiles.js
|
packages/lib/utils/credentialFiles.js
|
||||||
packages/lib/utils/joplinCloud.js
|
packages/lib/utils/joplinCloud.js
|
||||||
|
packages/lib/utils/processStartFlags.js
|
||||||
packages/lib/utils/userFetcher.js
|
packages/lib/utils/userFetcher.js
|
||||||
packages/lib/utils/webDAVUtils.test.js
|
packages/lib/utils/webDAVUtils.test.js
|
||||||
packages/lib/utils/webDAVUtils.js
|
packages/lib/utils/webDAVUtils.js
|
||||||
|
@ -9,7 +9,10 @@ const url = require('url');
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { dirname } = require('@joplin/lib/path-utils');
|
const { dirname } = require('@joplin/lib/path-utils');
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
const { ipcMain } = require('electron');
|
|
||||||
|
import { dialog, ipcMain } from 'electron';
|
||||||
|
import { _ } from '@joplin/lib/locale';
|
||||||
|
import restartInSafeModeFromMain from './utils/restartInSafeModeFromMain';
|
||||||
|
|
||||||
interface RendererProcessQuitReply {
|
interface RendererProcessQuitReply {
|
||||||
canClose: boolean;
|
canClose: boolean;
|
||||||
@ -34,7 +37,7 @@ export default class ElectronAppWrapper {
|
|||||||
private pluginWindows_: PluginWindows = {};
|
private pluginWindows_: PluginWindows = {};
|
||||||
private initialCallbackUrl_: string = null;
|
private initialCallbackUrl_: string = null;
|
||||||
|
|
||||||
public constructor(electronApp: any, env: string, profilePath: string, isDebugMode: boolean, initialCallbackUrl: string) {
|
public constructor(electronApp: any, env: string, profilePath: string|null, isDebugMode: boolean, initialCallbackUrl: string) {
|
||||||
this.electronApp_ = electronApp;
|
this.electronApp_ = electronApp;
|
||||||
this.env_ = env;
|
this.env_ = env;
|
||||||
this.isDebugMode_ = isDebugMode;
|
this.isDebugMode_ = isDebugMode;
|
||||||
@ -66,6 +69,42 @@ export default class ElectronAppWrapper {
|
|||||||
return this.initialCallbackUrl_;
|
return this.initialCallbackUrl_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Call when the app fails in a significant way.
|
||||||
|
//
|
||||||
|
// Assumes that the renderer process may be in an invalid state and so cannot
|
||||||
|
// be accessed.
|
||||||
|
public async handleAppFailure(errorMessage: string, canIgnore: boolean, isTesting?: boolean) {
|
||||||
|
const buttons = [];
|
||||||
|
buttons.push(_('Quit'));
|
||||||
|
const exitIndex = 0;
|
||||||
|
|
||||||
|
if (canIgnore) {
|
||||||
|
buttons.push(_('Ignore'));
|
||||||
|
}
|
||||||
|
const restartIndex = buttons.length;
|
||||||
|
buttons.push(_('Restart in safe mode'));
|
||||||
|
|
||||||
|
const { response } = await dialog.showMessageBox({
|
||||||
|
message: _('An error occurred: %s', errorMessage),
|
||||||
|
buttons,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response === restartIndex) {
|
||||||
|
await restartInSafeModeFromMain();
|
||||||
|
|
||||||
|
// A hung renderer seems to prevent the process from exiting completely.
|
||||||
|
// In this case, crashing the renderer allows the window to close.
|
||||||
|
//
|
||||||
|
// Also only run this if not testing (crashing the renderer breaks automated
|
||||||
|
// tests).
|
||||||
|
if (this.win_ && !this.win_.webContents.isCrashed() && !isTesting) {
|
||||||
|
this.win_.webContents.forcefullyCrashRenderer();
|
||||||
|
}
|
||||||
|
} else if (response === exitIndex) {
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public createWindow() {
|
public createWindow() {
|
||||||
// Set to true to view errors if the application does not start
|
// Set to true to view errors if the application does not start
|
||||||
const debugEarlyBugs = this.env_ === 'dev' || this.isDebugMode_;
|
const debugEarlyBugs = this.env_ === 'dev' || this.isDebugMode_;
|
||||||
@ -121,6 +160,20 @@ export default class ElectronAppWrapper {
|
|||||||
this.win_.setPosition(primaryDisplayWidth / 2 - windowWidth, primaryDisplayHeight / 2 - windowHeight);
|
this.win_.setPosition(primaryDisplayWidth / 2 - windowWidth, primaryDisplayHeight / 2 - windowHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.win_.webContents.on('unresponsive', async () => {
|
||||||
|
await this.handleAppFailure(_('Window unresponsive.'), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.win_.webContents.on('render-process-gone', async _event => {
|
||||||
|
await this.handleAppFailure('Renderer process gone.', false);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.win_.webContents.on('did-fail-load', async event => {
|
||||||
|
if ((event as any).isMainFrame) {
|
||||||
|
await this.handleAppFailure('Renderer process failed to load', false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
void this.win_.loadURL(url.format({
|
void this.win_.loadURL(url.format({
|
||||||
pathname: path.join(__dirname, 'index.html'),
|
pathname: path.join(__dirname, 'index.html'),
|
||||||
protocol: 'file:',
|
protocol: 'file:',
|
||||||
|
@ -2,6 +2,9 @@ import { test, expect } from './util/test';
|
|||||||
import MainScreen from './models/MainScreen';
|
import MainScreen from './models/MainScreen';
|
||||||
import activateMainMenuItem from './util/activateMainMenuItem';
|
import activateMainMenuItem from './util/activateMainMenuItem';
|
||||||
import SettingsScreen from './models/SettingsScreen';
|
import SettingsScreen from './models/SettingsScreen';
|
||||||
|
import { _electron as electron } from '@playwright/test';
|
||||||
|
import { writeFile } from 'fs-extra';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
|
|
||||||
test.describe('main', () => {
|
test.describe('main', () => {
|
||||||
@ -121,4 +124,23 @@ test.describe('main', () => {
|
|||||||
|
|
||||||
expect(await nextExternalUrlPromise).toBe(linkHref);
|
expect(await nextExternalUrlPromise).toBe(linkHref);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should start in safe mode if profile-dir/force-safe-mode-on-next-start exists', async ({ profileDirectory }) => {
|
||||||
|
await writeFile(join(profileDirectory, 'force-safe-mode-on-next-start'), 'true', 'utf8');
|
||||||
|
|
||||||
|
// We need to write to the force-safe-mode file before opening the Electron app.
|
||||||
|
// Open the app ourselves:
|
||||||
|
const startupArgs = [
|
||||||
|
'main.js', '--env', 'dev', '--profile', profileDirectory,
|
||||||
|
];
|
||||||
|
const electronApp = await electron.launch({ args: startupArgs });
|
||||||
|
const mainWindow = await electronApp.firstWindow();
|
||||||
|
|
||||||
|
const safeModeDisableLink = mainWindow.getByText('Disable safe mode and restart');
|
||||||
|
await safeModeDisableLink.waitFor();
|
||||||
|
await expect(safeModeDisableLink).toBeInViewport();
|
||||||
|
|
||||||
|
await electronApp.close();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import uuid from '@joplin/lib/uuid';
|
|||||||
|
|
||||||
|
|
||||||
type JoplinFixtures = {
|
type JoplinFixtures = {
|
||||||
|
profileDirectory: string;
|
||||||
electronApp: ElectronApplication;
|
electronApp: ElectronApplication;
|
||||||
mainWindow: Page;
|
mainWindow: Page;
|
||||||
};
|
};
|
||||||
@ -20,19 +21,26 @@ export const test = base.extend<JoplinFixtures>({
|
|||||||
// See https://github.com/microsoft/playwright/issues/8798
|
// See https://github.com/microsoft/playwright/issues/8798
|
||||||
//
|
//
|
||||||
// eslint-disable-next-line no-empty-pattern
|
// eslint-disable-next-line no-empty-pattern
|
||||||
electronApp: async ({ }, use) => {
|
profileDirectory: async ({ }, use) => {
|
||||||
const profilePath = resolve(join(dirname(__dirname), 'test-profile'));
|
const profilePath = resolve(join(dirname(__dirname), 'test-profile'));
|
||||||
const profileSubdir = join(profilePath, uuid.createNano());
|
const profileSubdir = join(profilePath, uuid.createNano());
|
||||||
await mkdirp(profileSubdir);
|
await mkdirp(profileSubdir);
|
||||||
|
|
||||||
const startupArgs = ['main.js', '--env', 'dev', '--profile', profileSubdir];
|
await use(profileSubdir);
|
||||||
|
|
||||||
|
await remove(profileSubdir);
|
||||||
|
},
|
||||||
|
|
||||||
|
electronApp: async ({ profileDirectory }, use) => {
|
||||||
|
const startupArgs = [
|
||||||
|
'main.js', '--env', 'dev', '--profile', profileDirectory,
|
||||||
|
];
|
||||||
const electronApp = await electron.launch({ args: startupArgs });
|
const electronApp = await electron.launch({ args: startupArgs });
|
||||||
|
|
||||||
await use(electronApp);
|
await use(electronApp);
|
||||||
|
|
||||||
await electronApp.firstWindow();
|
await electronApp.firstWindow();
|
||||||
await electronApp.close();
|
await electronApp.close();
|
||||||
await remove(profileSubdir);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
mainWindow: async ({ electronApp }, use) => {
|
mainWindow: async ({ electronApp }, use) => {
|
||||||
|
@ -31,117 +31,122 @@ const React = require('react');
|
|||||||
const nodeSqlite = require('sqlite3');
|
const nodeSqlite = require('sqlite3');
|
||||||
const initLib = require('@joplin/lib/initLib').default;
|
const initLib = require('@joplin/lib/initLib').default;
|
||||||
|
|
||||||
|
const main = async () => {
|
||||||
if (bridge().env() === 'dev') {
|
if (bridge().env() === 'dev') {
|
||||||
const newConsole = function(oldConsole) {
|
const newConsole = function(oldConsole) {
|
||||||
const output = {};
|
const output = {};
|
||||||
const fnNames = ['assert', 'clear', 'context', 'count', 'countReset', 'debug', 'dir', 'dirxml', 'error', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log', 'memory', 'profile', 'profileEnd', 'table', 'time', 'timeEnd', 'timeLog', 'timeStamp', 'trace', 'warn'];
|
const fnNames = ['assert', 'clear', 'context', 'count', 'countReset', 'debug', 'dir', 'dirxml', 'error', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log', 'memory', 'profile', 'profileEnd', 'table', 'time', 'timeEnd', 'timeLog', 'timeStamp', 'trace', 'warn'];
|
||||||
for (const fnName of fnNames) {
|
for (const fnName of fnNames) {
|
||||||
if (fnName === 'warn') {
|
if (fnName === 'warn') {
|
||||||
output.warn = function(...text) {
|
output.warn = function(...text) {
|
||||||
const s = [...text].join('');
|
const s = [...text].join('');
|
||||||
// React spams the console with walls of warnings even outside of strict mode, and even after having renamed
|
// React spams the console with walls of warnings even outside of strict mode, and even after having renamed
|
||||||
// unsafe methods to UNSAFE_xxxx, so we need to hack the console to remove them...
|
// unsafe methods to UNSAFE_xxxx, so we need to hack the console to remove them...
|
||||||
if (s.indexOf('Warning: componentWillReceiveProps has been renamed, and is not recommended for use') === 0) return;
|
if (s.indexOf('Warning: componentWillReceiveProps has been renamed, and is not recommended for use') === 0) return;
|
||||||
if (s.indexOf('Warning: componentWillUpdate has been renamed, and is not recommended for use.') === 0) return;
|
if (s.indexOf('Warning: componentWillUpdate has been renamed, and is not recommended for use.') === 0) return;
|
||||||
oldConsole.warn(...text);
|
oldConsole.warn(...text);
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
output[fnName] = function(...text) {
|
output[fnName] = function(...text) {
|
||||||
return oldConsole[fnName](...text);
|
return oldConsole[fnName](...text);
|
||||||
};
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
return output;
|
||||||
return output;
|
}(window.console);
|
||||||
}(window.console);
|
|
||||||
|
|
||||||
window.console = newConsole;
|
window.console = newConsole;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.info(`Environment: ${bridge().env()}`);
|
console.info(`Environment: ${bridge().env()}`);
|
||||||
|
|
||||||
const fsDriver = new FsDriverNode();
|
const fsDriver = new FsDriverNode();
|
||||||
Logger.fsDriver_ = fsDriver;
|
Logger.fsDriver_ = fsDriver;
|
||||||
Resource.fsDriver_ = fsDriver;
|
Resource.fsDriver_ = fsDriver;
|
||||||
EncryptionService.fsDriver_ = fsDriver;
|
EncryptionService.fsDriver_ = fsDriver;
|
||||||
FileApiDriverLocal.fsDriver_ = fsDriver;
|
FileApiDriverLocal.fsDriver_ = fsDriver;
|
||||||
|
|
||||||
// That's not good, but it's to avoid circular dependency issues
|
// That's not good, but it's to avoid circular dependency issues
|
||||||
// in the BaseItem class.
|
// in the BaseItem class.
|
||||||
BaseItem.loadClass('Note', Note);
|
BaseItem.loadClass('Note', Note);
|
||||||
BaseItem.loadClass('Folder', Folder);
|
BaseItem.loadClass('Folder', Folder);
|
||||||
BaseItem.loadClass('Resource', Resource);
|
BaseItem.loadClass('Resource', Resource);
|
||||||
BaseItem.loadClass('Tag', Tag);
|
BaseItem.loadClass('Tag', Tag);
|
||||||
BaseItem.loadClass('NoteTag', NoteTag);
|
BaseItem.loadClass('NoteTag', NoteTag);
|
||||||
BaseItem.loadClass('MasterKey', MasterKey);
|
BaseItem.loadClass('MasterKey', MasterKey);
|
||||||
BaseItem.loadClass('Revision', Revision);
|
BaseItem.loadClass('Revision', Revision);
|
||||||
|
|
||||||
Setting.setConstant('appId', `net.cozic.joplin${bridge().env() === 'dev' ? 'dev' : ''}-desktop`);
|
Setting.setConstant('appId', `net.cozic.joplin${bridge().env() === 'dev' ? 'dev' : ''}-desktop`);
|
||||||
Setting.setConstant('appType', 'desktop');
|
Setting.setConstant('appType', 'desktop');
|
||||||
|
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.info(`appId: ${Setting.value('appId')}`);
|
console.info(`appId: ${Setting.value('appId')}`);
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.info(`appType: ${Setting.value('appType')}`);
|
console.info(`appType: ${Setting.value('appType')}`);
|
||||||
|
|
||||||
let keytar;
|
let keytar;
|
||||||
try {
|
try {
|
||||||
keytar = shim.platformSupportsKeyChain() ? require('keytar') : null;
|
keytar = shim.platformSupportsKeyChain() ? require('keytar') : null;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Cannot load keytar - keychain support will be disabled', error);
|
console.error('Cannot load keytar - keychain support will be disabled', error);
|
||||||
keytar = null;
|
keytar = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function appVersion() {
|
function appVersion() {
|
||||||
const p = require('./packageInfo.js');
|
const p = require('./packageInfo.js');
|
||||||
return p.version;
|
return p.version;
|
||||||
}
|
}
|
||||||
|
|
||||||
shimInit({
|
shimInit({
|
||||||
keytar,
|
keytar,
|
||||||
React,
|
React,
|
||||||
appVersion,
|
appVersion,
|
||||||
electronBridge: bridge(),
|
electronBridge: bridge(),
|
||||||
nodeSqlite,
|
nodeSqlite,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Disable drag and drop of links inside application (which would
|
// Disable drag and drop of links inside application (which would
|
||||||
// open it as if the whole app was a browser)
|
// open it as if the whole app was a browser)
|
||||||
document.addEventListener('dragover', event => event.preventDefault());
|
document.addEventListener('dragover', event => event.preventDefault());
|
||||||
document.addEventListener('drop', event => event.preventDefault());
|
document.addEventListener('drop', event => event.preventDefault());
|
||||||
|
|
||||||
// Disable middle-click (which would open a new browser window, but we don't want this)
|
// Disable middle-click (which would open a new browser window, but we don't want this)
|
||||||
document.addEventListener('auxclick', event => event.preventDefault());
|
document.addEventListener('auxclick', event => event.preventDefault());
|
||||||
|
|
||||||
// Each link (rendered as a button or list item) has its own custom click event
|
// Each link (rendered as a button or list item) has its own custom click event
|
||||||
// so disable the default. In particular this will disable Ctrl+Clicking a link
|
// so disable the default. In particular this will disable Ctrl+Clicking a link
|
||||||
// which would open a new browser window.
|
// which would open a new browser window.
|
||||||
document.addEventListener('click', (event) => {
|
document.addEventListener('click', (event) => {
|
||||||
// We don't apply this to labels and inputs because it would break
|
// We don't apply this to labels and inputs because it would break
|
||||||
// checkboxes. Such a global event handler is probably not a good idea
|
// checkboxes. Such a global event handler is probably not a good idea
|
||||||
// anyway but keeping it for now, as it doesn't seem to break anything else.
|
// anyway but keeping it for now, as it doesn't seem to break anything else.
|
||||||
// https://github.com/facebook/react/issues/13477#issuecomment-489274045
|
// https://github.com/facebook/react/issues/13477#issuecomment-489274045
|
||||||
if (['LABEL', 'INPUT'].includes(event.target.nodeName)) return;
|
if (['LABEL', 'INPUT'].includes(event.target.nodeName)) return;
|
||||||
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
});
|
});
|
||||||
|
|
||||||
const logger = new Logger();
|
const logger = new Logger();
|
||||||
Logger.initializeGlobalLogger(logger);
|
Logger.initializeGlobalLogger(logger);
|
||||||
initLib(logger);
|
initLib(logger);
|
||||||
|
|
||||||
app().start(bridge().processArgv()).then((result) => {
|
const startResult = await app().start(bridge().processArgv());
|
||||||
if (!result || !result.action) {
|
|
||||||
|
if (!startResult || !startResult.action) {
|
||||||
require('./gui/Root');
|
require('./gui/Root');
|
||||||
} else if (result.action === 'upgradeSyncTarget') {
|
} else if (startResult.action === 'upgradeSyncTarget') {
|
||||||
require('./gui/Root_UpgradeSyncTarget');
|
require('./gui/Root_UpgradeSyncTarget');
|
||||||
}
|
}
|
||||||
}).catch((error) => {
|
};
|
||||||
const env = bridge().env();
|
|
||||||
|
|
||||||
|
main().catch((error) => {
|
||||||
|
const env = bridge().env();
|
||||||
|
console.error(error);
|
||||||
|
|
||||||
|
let errorMessage;
|
||||||
if (error.code === 'flagError') {
|
if (error.code === 'flagError') {
|
||||||
bridge().showErrorMessageBox(error.message);
|
errorMessage = error.message;
|
||||||
} else {
|
} else {
|
||||||
// If something goes wrong at this stage we don't have a console or a log file
|
// If something goes wrong at this stage we don't have a console or a log file
|
||||||
// so display the error in a message box.
|
// so display the error in a message box.
|
||||||
@ -150,13 +155,12 @@ app().start(bridge().processArgv()).then((result) => {
|
|||||||
if (error.lineNumber) msg.push(error.lineNumber);
|
if (error.lineNumber) msg.push(error.lineNumber);
|
||||||
if (error.stack) msg.push(error.stack);
|
if (error.stack) msg.push(error.stack);
|
||||||
|
|
||||||
if (env === 'dev') {
|
errorMessage = msg.join('\n\n');
|
||||||
console.error(error);
|
|
||||||
} else {
|
|
||||||
bridge().showErrorMessageBox(msg.join('\n\n'));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// In dev, we leave the app open as debug statements in the console can be useful
|
// In dev, we give the option to leave the app open as debug statements in the
|
||||||
if (env !== 'dev') bridge().electronApp().exit(1);
|
// console can be useful
|
||||||
|
const canIgnore = env === 'dev';
|
||||||
|
bridge().electronApp().handleAppFailure(errorMessage, canIgnore);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
45
packages/app-desktop/utils/restartInSafeModeFromMain.test.ts
Normal file
45
packages/app-desktop/utils/restartInSafeModeFromMain.test.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
|
||||||
|
let currentProfileDirectory: string;
|
||||||
|
|
||||||
|
jest.doMock('../bridge', () => ({
|
||||||
|
// Mock the bridge functions used by restartInSafeModeFromMain
|
||||||
|
// to remove the dependency on Electron.
|
||||||
|
default: () => ({
|
||||||
|
restart: jest.fn(),
|
||||||
|
processArgv: () => [
|
||||||
|
// The argument parser expects the first two arguments to
|
||||||
|
// be the path to NodeJS and the second to be the main filename.
|
||||||
|
process.argv[0], __filename,
|
||||||
|
|
||||||
|
// Only the following arguments are used.
|
||||||
|
'--profile', currentProfileDirectory,
|
||||||
|
],
|
||||||
|
env: () => 'dev',
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
import { mkdtemp, readFile, remove } from 'fs-extra';
|
||||||
|
import restartInSafeModeFromMain from './restartInSafeModeFromMain';
|
||||||
|
import { tmpdir } from 'os';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { safeModeFlagFilename } from '@joplin/lib/BaseApplication';
|
||||||
|
|
||||||
|
|
||||||
|
describe('restartInSafeModeFromMain', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
currentProfileDirectory = await mkdtemp(join(tmpdir(), 'safemode-restart-test'));
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await remove(currentProfileDirectory);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should create a safe mode flag file', async () => {
|
||||||
|
await restartInSafeModeFromMain();
|
||||||
|
const safeModeFlagFilepath = join(
|
||||||
|
currentProfileDirectory, safeModeFlagFilename,
|
||||||
|
);
|
||||||
|
expect(await readFile(safeModeFlagFilepath, 'utf8')).toBe('true');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
33
packages/app-desktop/utils/restartInSafeModeFromMain.ts
Normal file
33
packages/app-desktop/utils/restartInSafeModeFromMain.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import Setting from '@joplin/lib/models/Setting';
|
||||||
|
import bridge from '../bridge';
|
||||||
|
import processStartFlags from '@joplin/lib/utils/processStartFlags';
|
||||||
|
import BaseApplication, { safeModeFlagFilename } from '@joplin/lib/BaseApplication';
|
||||||
|
import initProfile from '@joplin/lib/services/profileConfig/initProfile';
|
||||||
|
import { writeFile } from 'fs-extra';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
|
|
||||||
|
const restartInSafeModeFromMain = async () => {
|
||||||
|
// Only set constants here -- the main process doesn't have easy access (without loading
|
||||||
|
// a large amount of other code) to the database.
|
||||||
|
const appName = `joplin${bridge().env() === 'dev' ? 'dev' : ''}-desktop`;
|
||||||
|
Setting.setConstant('appId', `net.cozic.${appName}`);
|
||||||
|
Setting.setConstant('appType', 'desktop');
|
||||||
|
Setting.setConstant('appName', appName);
|
||||||
|
|
||||||
|
// Load just enough for us to write a file in the profile directory
|
||||||
|
const { shimInit } = require('@joplin/lib/shim-init-node.js');
|
||||||
|
shimInit({});
|
||||||
|
|
||||||
|
const startFlags = await processStartFlags(bridge().processArgv());
|
||||||
|
const rootProfileDir = BaseApplication.determineProfileDir(startFlags.matched);
|
||||||
|
const { profileDir } = await initProfile(rootProfileDir);
|
||||||
|
|
||||||
|
// We can't access the database, so write to a file instead.
|
||||||
|
const safeModeFlagFile = join(profileDir, safeModeFlagFilename);
|
||||||
|
await writeFile(safeModeFlagFile, 'true', 'utf8');
|
||||||
|
|
||||||
|
bridge().restart();
|
||||||
|
};
|
||||||
|
|
||||||
|
export default restartInSafeModeFromMain;
|
@ -6,7 +6,7 @@ import BaseService from './services/BaseService';
|
|||||||
import reducer, { getNotesParent, serializeNotesParent, setStore, State } from './reducer';
|
import reducer, { getNotesParent, serializeNotesParent, setStore, State } from './reducer';
|
||||||
import KeychainServiceDriver from './services/keychain/KeychainServiceDriver.node';
|
import KeychainServiceDriver from './services/keychain/KeychainServiceDriver.node';
|
||||||
import KeychainServiceDriverDummy from './services/keychain/KeychainServiceDriver.dummy';
|
import KeychainServiceDriverDummy from './services/keychain/KeychainServiceDriver.dummy';
|
||||||
import { _, setLocale } from './locale';
|
import { setLocale } from './locale';
|
||||||
import KvStore from './services/KvStore';
|
import KvStore from './services/KvStore';
|
||||||
import SyncTargetJoplinServer from './SyncTargetJoplinServer';
|
import SyncTargetJoplinServer from './SyncTargetJoplinServer';
|
||||||
import SyncTargetOneDrive from './SyncTargetOneDrive';
|
import SyncTargetOneDrive from './SyncTargetOneDrive';
|
||||||
@ -26,8 +26,7 @@ import time from './time';
|
|||||||
import BaseSyncTarget from './BaseSyncTarget';
|
import BaseSyncTarget from './BaseSyncTarget';
|
||||||
const reduxSharedMiddleware = require('./components/shared/reduxSharedMiddleware');
|
const reduxSharedMiddleware = require('./components/shared/reduxSharedMiddleware');
|
||||||
const os = require('os');
|
const os = require('os');
|
||||||
const fs = require('fs-extra');
|
import fs = require('fs-extra');
|
||||||
import JoplinError from './JoplinError';
|
|
||||||
const EventEmitter = require('events');
|
const EventEmitter = require('events');
|
||||||
const syswidecas = require('./vendor/syswide-cas');
|
const syswidecas = require('./vendor/syswide-cas');
|
||||||
import SyncTargetRegistry from './SyncTargetRegistry';
|
import SyncTargetRegistry from './SyncTargetRegistry';
|
||||||
@ -60,6 +59,8 @@ import { ProfileConfig } from './services/profileConfig/types';
|
|||||||
import initProfile from './services/profileConfig/initProfile';
|
import initProfile from './services/profileConfig/initProfile';
|
||||||
import { parseShareCache } from './services/share/reducer';
|
import { parseShareCache } from './services/share/reducer';
|
||||||
import RotatingLogs from './RotatingLogs';
|
import RotatingLogs from './RotatingLogs';
|
||||||
|
import { join } from 'path';
|
||||||
|
import processStartFlags, { MatchedStartFlags } from './utils/processStartFlags';
|
||||||
|
|
||||||
const appLogger: LoggerWrapper = Logger.create('App');
|
const appLogger: LoggerWrapper = Logger.create('App');
|
||||||
|
|
||||||
@ -70,6 +71,7 @@ interface StartOptions {
|
|||||||
keychainEnabled?: boolean;
|
keychainEnabled?: boolean;
|
||||||
setupGlobalLogger?: boolean;
|
setupGlobalLogger?: boolean;
|
||||||
}
|
}
|
||||||
|
export const safeModeFlagFilename = 'force-safe-mode-on-next-start';
|
||||||
|
|
||||||
export default class BaseApplication {
|
export default class BaseApplication {
|
||||||
|
|
||||||
@ -163,154 +165,15 @@ export default class BaseApplication {
|
|||||||
// Handles the initial flags passed to main script and
|
// Handles the initial flags passed to main script and
|
||||||
// returns the remaining args.
|
// returns the remaining args.
|
||||||
private async handleStartFlags_(argv: string[], setDefaults = true) {
|
private async handleStartFlags_(argv: string[], setDefaults = true) {
|
||||||
const matched: any = {};
|
const flags = await processStartFlags(argv, setDefaults);
|
||||||
argv = argv.slice(0);
|
|
||||||
argv.splice(0, 2); // First arguments are the node executable, and the node JS file
|
|
||||||
|
|
||||||
while (argv.length) {
|
if (flags.matched.showStackTraces) {
|
||||||
const arg = argv[0];
|
this.showStackTraces_ = true;
|
||||||
const nextArg = argv.length >= 2 ? argv[1] : null;
|
|
||||||
|
|
||||||
if (arg === '--profile') {
|
|
||||||
if (!nextArg) throw new JoplinError(_('Usage: %s', '--profile <dir-path>'), 'flagError');
|
|
||||||
matched.profileDir = nextArg;
|
|
||||||
argv.splice(0, 2);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg === '--no-welcome') {
|
|
||||||
matched.welcomeDisabled = true;
|
|
||||||
argv.splice(0, 1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg === '--env') {
|
|
||||||
if (!nextArg) throw new JoplinError(_('Usage: %s', '--env <dev|prod>'), 'flagError');
|
|
||||||
matched.env = nextArg;
|
|
||||||
argv.splice(0, 2);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg === '--is-demo') {
|
|
||||||
Setting.setConstant('isDemo', true);
|
|
||||||
argv.splice(0, 1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg === '--safe-mode') {
|
|
||||||
matched.isSafeMode = true;
|
|
||||||
argv.splice(0, 1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg === '--open-dev-tools') {
|
|
||||||
Setting.setConstant('flagOpenDevTools', true);
|
|
||||||
argv.splice(0, 1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg === '--debug') {
|
|
||||||
// Currently only handled by ElectronAppWrapper (isDebugMode property)
|
|
||||||
argv.splice(0, 1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg === '--update-geolocation-disabled') {
|
|
||||||
Note.updateGeolocationEnabled_ = false;
|
|
||||||
argv.splice(0, 1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg === '--stack-trace-enabled') {
|
|
||||||
this.showStackTraces_ = true;
|
|
||||||
argv.splice(0, 1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg === '--log-level') {
|
|
||||||
if (!nextArg) throw new JoplinError(_('Usage: %s', '--log-level <none|error|warn|info|debug>'), 'flagError');
|
|
||||||
matched.logLevel = Logger.levelStringToId(nextArg);
|
|
||||||
argv.splice(0, 2);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg.indexOf('-psn') === 0) {
|
|
||||||
// Some weird flag passed by macOS - can be ignored.
|
|
||||||
// https://github.com/laurent22/joplin/issues/480
|
|
||||||
// https://stackoverflow.com/questions/10242115
|
|
||||||
argv.splice(0, 1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg === '--enable-logging') {
|
|
||||||
// Electron-specific flag used for debugging - ignore it
|
|
||||||
argv.splice(0, 1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg === '--dev-plugins') {
|
|
||||||
Setting.setConstant('startupDevPlugins', nextArg.split(',').map(p => p.trim()));
|
|
||||||
argv.splice(0, 2);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg.indexOf('--remote-debugging-port=') === 0) {
|
|
||||||
// Electron-specific flag used for debugging - ignore it. Electron expects this flag in '--x=y' form, a single string.
|
|
||||||
argv.splice(0, 1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg === '--no-sandbox') {
|
|
||||||
// Electron-specific flag for running the app without chrome-sandbox
|
|
||||||
// Allows users to use it as a workaround for the electron+AppImage issue
|
|
||||||
// https://github.com/laurent22/joplin/issues/2246
|
|
||||||
argv.splice(0, 1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg.indexOf('--user-data-dir=') === 0) {
|
|
||||||
// Electron-specific flag. Allows users to run the app with chromedriver.
|
|
||||||
argv.splice(0, 1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg.indexOf('--enable-features=') === 0) {
|
|
||||||
// Electron-specific flag - ignore it
|
|
||||||
// Allows users to run the app on native wayland
|
|
||||||
argv.splice(0, 1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg.indexOf('--ozone-platform=') === 0) {
|
|
||||||
// Electron-specific flag - ignore it
|
|
||||||
// Allows users to run the app on native wayland
|
|
||||||
argv.splice(0, 1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg === '--disable-smooth-scrolling') {
|
|
||||||
// Electron-specific flag - ignore it
|
|
||||||
// Allows users to disable smooth scrolling
|
|
||||||
argv.splice(0, 1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg.length && arg[0] === '-') {
|
|
||||||
throw new JoplinError(_('Unknown flag: %s', arg), 'flagError');
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (setDefaults) {
|
|
||||||
if (!matched.logLevel) matched.logLevel = Logger.LEVEL_INFO;
|
|
||||||
if (!matched.env) matched.env = 'prod';
|
|
||||||
if (!matched.devPlugins) matched.devPlugins = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
matched: matched,
|
matched: flags.matched,
|
||||||
argv: argv,
|
argv: flags.argv,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -725,7 +588,7 @@ export default class BaseApplication {
|
|||||||
return flags.matched;
|
return flags.matched;
|
||||||
}
|
}
|
||||||
|
|
||||||
public determineProfileDir(initArgs: any) {
|
public static determineProfileDir(initArgs: MatchedStartFlags) {
|
||||||
let output = '';
|
let output = '';
|
||||||
|
|
||||||
if (initArgs.profileDir) {
|
if (initArgs.profileDir) {
|
||||||
@ -773,7 +636,7 @@ export default class BaseApplication {
|
|||||||
// https://immerjs.github.io/immer/docs/freezing
|
// https://immerjs.github.io/immer/docs/freezing
|
||||||
setAutoFreeze(initArgs.env === 'dev');
|
setAutoFreeze(initArgs.env === 'dev');
|
||||||
|
|
||||||
const rootProfileDir = this.determineProfileDir(initArgs);
|
const rootProfileDir = BaseApplication.determineProfileDir(initArgs);
|
||||||
const { profileDir, profileConfig, isSubProfile } = await initProfile(rootProfileDir);
|
const { profileDir, profileConfig, isSubProfile } = await initProfile(rootProfileDir);
|
||||||
this.profileConfig_ = profileConfig;
|
this.profileConfig_ = profileConfig;
|
||||||
|
|
||||||
@ -863,6 +726,13 @@ export default class BaseApplication {
|
|||||||
Setting.setValue('isSafeMode', true);
|
Setting.setValue('isSafeMode', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const safeModeFlagFile = join(profileDir, safeModeFlagFilename);
|
||||||
|
if (await fs.pathExists(safeModeFlagFile) && fs.readFileSync(safeModeFlagFile, 'utf8') === 'true') {
|
||||||
|
appLogger.info(`Safe mode enabled because of file: ${safeModeFlagFile}`);
|
||||||
|
Setting.setValue('isSafeMode', true);
|
||||||
|
fs.removeSync(safeModeFlagFile);
|
||||||
|
}
|
||||||
|
|
||||||
if (Setting.value('firstStart')) {
|
if (Setting.value('firstStart')) {
|
||||||
const locale = shim.detectAndSetLocale(Setting);
|
const locale = shim.detectAndSetLocale(Setting);
|
||||||
reg.logger().info(`First start: detected locale as ${locale}`);
|
reg.logger().info(`First start: detected locale as ${locale}`);
|
||||||
|
174
packages/lib/utils/processStartFlags.ts
Normal file
174
packages/lib/utils/processStartFlags.ts
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import Logger, { LogLevel } from '@joplin/utils/Logger';
|
||||||
|
import JoplinError from '../JoplinError';
|
||||||
|
import { _ } from '../locale';
|
||||||
|
import Setting from '../models/Setting';
|
||||||
|
import Note from '../models/Note';
|
||||||
|
|
||||||
|
export interface MatchedStartFlags {
|
||||||
|
profileDir?: string;
|
||||||
|
welcomeDisabled?: boolean;
|
||||||
|
env?: string;
|
||||||
|
isSafeMode?: boolean;
|
||||||
|
showStackTraces?: boolean;
|
||||||
|
logLevel?: LogLevel;
|
||||||
|
devPlugins?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handles the initial flags passed to main script and
|
||||||
|
// returns the remaining args.
|
||||||
|
const processStartFlags = async (argv: string[], setDefaults = true) => {
|
||||||
|
const matched: MatchedStartFlags = {};
|
||||||
|
argv = argv.slice(0);
|
||||||
|
argv.splice(0, 2); // First arguments are the node executable, and the node JS file
|
||||||
|
|
||||||
|
while (argv.length) {
|
||||||
|
const arg = argv[0];
|
||||||
|
const nextArg = argv.length >= 2 ? argv[1] : null;
|
||||||
|
|
||||||
|
if (arg === '--profile') {
|
||||||
|
if (!nextArg) throw new JoplinError(_('Usage: %s', '--profile <dir-path>'), 'flagError');
|
||||||
|
matched.profileDir = nextArg;
|
||||||
|
argv.splice(0, 2);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--no-welcome') {
|
||||||
|
matched.welcomeDisabled = true;
|
||||||
|
argv.splice(0, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--env') {
|
||||||
|
if (!nextArg) throw new JoplinError(_('Usage: %s', '--env <dev|prod>'), 'flagError');
|
||||||
|
matched.env = nextArg;
|
||||||
|
argv.splice(0, 2);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--is-demo') {
|
||||||
|
Setting.setConstant('isDemo', true);
|
||||||
|
argv.splice(0, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--safe-mode') {
|
||||||
|
matched.isSafeMode = true;
|
||||||
|
argv.splice(0, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--open-dev-tools') {
|
||||||
|
Setting.setConstant('flagOpenDevTools', true);
|
||||||
|
argv.splice(0, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--debug') {
|
||||||
|
// Currently only handled by ElectronAppWrapper (isDebugMode property)
|
||||||
|
argv.splice(0, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--update-geolocation-disabled') {
|
||||||
|
Note.updateGeolocationEnabled_ = false;
|
||||||
|
argv.splice(0, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--stack-trace-enabled') {
|
||||||
|
matched.showStackTraces = true;
|
||||||
|
argv.splice(0, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--log-level') {
|
||||||
|
if (!nextArg) throw new JoplinError(_('Usage: %s', '--log-level <none|error|warn|info|debug>'), 'flagError');
|
||||||
|
matched.logLevel = Logger.levelStringToId(nextArg);
|
||||||
|
argv.splice(0, 2);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg.indexOf('-psn') === 0) {
|
||||||
|
// Some weird flag passed by macOS - can be ignored.
|
||||||
|
// https://github.com/laurent22/joplin/issues/480
|
||||||
|
// https://stackoverflow.com/questions/10242115
|
||||||
|
argv.splice(0, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--enable-logging') {
|
||||||
|
// Electron-specific flag used for debugging - ignore it
|
||||||
|
argv.splice(0, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--dev-plugins') {
|
||||||
|
matched.devPlugins = nextArg.split(',').map(p => p.trim());
|
||||||
|
Setting.setConstant('startupDevPlugins', matched.devPlugins);
|
||||||
|
argv.splice(0, 2);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg.indexOf('--remote-debugging-port=') === 0) {
|
||||||
|
// Electron-specific flag used for debugging - ignore it. Electron expects this flag in '--x=y' form, a single string.
|
||||||
|
argv.splice(0, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--no-sandbox') {
|
||||||
|
// Electron-specific flag for running the app without chrome-sandbox
|
||||||
|
// Allows users to use it as a workaround for the electron+AppImage issue
|
||||||
|
// https://github.com/laurent22/joplin/issues/2246
|
||||||
|
argv.splice(0, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg.indexOf('--user-data-dir=') === 0) {
|
||||||
|
// Electron-specific flag. Allows users to run the app with chromedriver.
|
||||||
|
argv.splice(0, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg.indexOf('--enable-features=') === 0) {
|
||||||
|
// Electron-specific flag - ignore it
|
||||||
|
// Allows users to run the app on native wayland
|
||||||
|
argv.splice(0, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg.indexOf('--ozone-platform=') === 0) {
|
||||||
|
// Electron-specific flag - ignore it
|
||||||
|
// Allows users to run the app on native wayland
|
||||||
|
argv.splice(0, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--disable-smooth-scrolling') {
|
||||||
|
// Electron-specific flag - ignore it
|
||||||
|
// Allows users to disable smooth scrolling
|
||||||
|
argv.splice(0, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg.length && arg[0] === '-') {
|
||||||
|
throw new JoplinError(_('Unknown flag: %s', arg), 'flagError');
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setDefaults) {
|
||||||
|
if (!matched.logLevel) matched.logLevel = Logger.LEVEL_INFO;
|
||||||
|
if (!matched.env) matched.env = 'prod';
|
||||||
|
if (!matched.devPlugins) matched.devPlugins = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
matched: matched,
|
||||||
|
argv: argv,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default processStartFlags;
|
Loading…
x
Reference in New Issue
Block a user