2017-01-31 07:50:28 +01:00
|
|
|
const electron = require('electron');
|
|
|
|
const app = electron.app;
|
|
|
|
const path = require('path');
|
|
|
|
const fs = require('fs');
|
2015-10-21 22:32:02 +02:00
|
|
|
|
2017-01-31 07:50:28 +01:00
|
|
|
let mainWindow = null;
|
|
|
|
let appIcon = null;
|
|
|
|
let openFile = process.argv.filter(arg => /\.kdbx$/i.test(arg))[0];
|
|
|
|
let ready = false;
|
|
|
|
let appReady = false;
|
|
|
|
let restartPending = false;
|
|
|
|
let mainWindowPosition = {};
|
|
|
|
let updateMainWindowPositionTimeout = null;
|
2017-06-05 13:33:12 +02:00
|
|
|
const userDataDir = app.getPath('userData');
|
|
|
|
const windowPositionFileName = path.join(userDataDir, 'window-position.json');
|
|
|
|
const appSettingsFileName = path.join(userDataDir, 'app-settings.json');
|
|
|
|
const tempUserDataPath = path.join(userDataDir, 'temp');
|
|
|
|
const tempUserDataPathRand = Date.now().toString() + Math.random().toString();
|
2015-10-25 20:26:33 +01:00
|
|
|
|
2016-08-13 16:28:06 +02:00
|
|
|
let htmlPath = process.argv.filter(arg => arg.startsWith('--htmlpath=')).map(arg => arg.replace('--htmlpath=', ''))[0];
|
|
|
|
if (!htmlPath) {
|
|
|
|
htmlPath = 'file://' + path.join(__dirname, 'index.html');
|
|
|
|
}
|
2015-10-21 22:32:02 +02:00
|
|
|
|
2017-06-05 13:33:12 +02:00
|
|
|
app.setPath('userData', path.join(tempUserDataPath, tempUserDataPathRand));
|
|
|
|
|
2016-07-17 13:30:38 +02:00
|
|
|
app.on('window-all-closed', () => {
|
2016-07-16 18:39:52 +02:00
|
|
|
if (restartPending) {
|
|
|
|
// unbind all handlers, load new app.js module and pass control to it
|
|
|
|
app.removeAllListeners('window-all-closed');
|
|
|
|
app.removeAllListeners('ready');
|
|
|
|
app.removeAllListeners('open-file');
|
|
|
|
app.removeAllListeners('activate');
|
2016-08-20 09:07:01 +02:00
|
|
|
electron.globalShortcut.unregisterAll();
|
|
|
|
electron.powerMonitor.removeAllListeners('suspend');
|
|
|
|
electron.powerMonitor.removeAllListeners('resume');
|
2017-06-05 13:33:12 +02:00
|
|
|
const userDataAppFile = path.join(userDataDir, 'app.js');
|
2016-07-16 18:39:52 +02:00
|
|
|
delete require.cache[require.resolve('./app.js')];
|
|
|
|
require(userDataAppFile);
|
|
|
|
app.emit('ready');
|
|
|
|
} else {
|
2016-04-16 22:34:16 +02:00
|
|
|
if (process.platform !== 'darwin') {
|
2016-07-16 18:39:52 +02:00
|
|
|
app.quit();
|
2016-04-16 22:34:16 +02:00
|
|
|
}
|
2016-07-16 18:39:52 +02:00
|
|
|
}
|
|
|
|
});
|
2016-07-17 13:30:38 +02:00
|
|
|
app.on('ready', () => {
|
2016-07-16 18:39:52 +02:00
|
|
|
if (!checkSingleInstance()) {
|
2016-12-26 10:51:59 +01:00
|
|
|
appReady = true;
|
2016-07-16 18:39:52 +02:00
|
|
|
setAppOptions();
|
|
|
|
createMainWindow();
|
|
|
|
setGlobalShortcuts();
|
2016-08-20 09:04:30 +02:00
|
|
|
subscribePowerEvents();
|
2017-06-05 13:33:12 +02:00
|
|
|
deleteOldTempFiles();
|
2016-07-16 18:39:52 +02:00
|
|
|
}
|
|
|
|
});
|
2016-07-17 13:30:38 +02:00
|
|
|
app.on('open-file', (e, path) => {
|
2016-07-16 18:39:52 +02:00
|
|
|
e.preventDefault();
|
|
|
|
openFile = path;
|
|
|
|
notifyOpenFile();
|
|
|
|
});
|
2016-07-17 13:30:38 +02:00
|
|
|
app.on('activate', () => {
|
2016-07-16 18:39:52 +02:00
|
|
|
if (process.platform === 'darwin') {
|
2016-12-26 10:51:59 +01:00
|
|
|
if (appReady && !mainWindow) {
|
2016-07-16 18:39:52 +02:00
|
|
|
createMainWindow();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2016-07-17 13:30:38 +02:00
|
|
|
app.on('will-quit', () => {
|
2016-07-16 18:39:52 +02:00
|
|
|
electron.globalShortcut.unregisterAll();
|
|
|
|
});
|
|
|
|
app.restartApp = function () {
|
|
|
|
restartPending = true;
|
|
|
|
mainWindow.close();
|
2016-07-17 13:30:38 +02:00
|
|
|
setTimeout(() => {
|
2016-07-16 18:39:52 +02:00
|
|
|
restartPending = false;
|
|
|
|
}, 1000);
|
|
|
|
};
|
|
|
|
app.openWindow = function (opts) {
|
|
|
|
return new electron.BrowserWindow(opts);
|
|
|
|
};
|
|
|
|
app.minimizeApp = function () {
|
|
|
|
if (process.platform !== 'darwin') {
|
|
|
|
mainWindow.minimize();
|
|
|
|
mainWindow.setSkipTaskbar(true);
|
|
|
|
appIcon = new electron.Tray(path.join(__dirname, 'icon.png'));
|
|
|
|
appIcon.on('click', restoreMainWindow);
|
2017-01-31 07:50:28 +01:00
|
|
|
const contextMenu = electron.Menu.buildFromTemplate([
|
2016-07-16 18:39:52 +02:00
|
|
|
{label: 'Open KeeWeb', click: restoreMainWindow},
|
|
|
|
{label: 'Quit KeeWeb', click: closeMainWindow}
|
|
|
|
]);
|
|
|
|
appIcon.setContextMenu(contextMenu);
|
|
|
|
appIcon.setToolTip('KeeWeb');
|
|
|
|
}
|
|
|
|
};
|
|
|
|
app.getMainWindow = function () {
|
|
|
|
return mainWindow;
|
|
|
|
};
|
2016-07-24 19:11:25 +02:00
|
|
|
app.emitBackboneEvent = emitBackboneEvent;
|
2015-10-24 21:06:44 +02:00
|
|
|
|
2016-04-15 22:40:55 +02:00
|
|
|
function checkSingleInstance() {
|
2017-01-31 07:50:28 +01:00
|
|
|
const shouldQuit = app.makeSingleInstance((/* commandLine, workingDirectory */) => {
|
2016-04-15 22:40:55 +02:00
|
|
|
restoreMainWindow();
|
|
|
|
});
|
|
|
|
|
|
|
|
if (shouldQuit) {
|
|
|
|
app.quit();
|
|
|
|
}
|
|
|
|
return shouldQuit;
|
|
|
|
}
|
|
|
|
|
2016-04-10 09:37:55 +02:00
|
|
|
function setAppOptions() {
|
|
|
|
app.commandLine.appendSwitch('disable-background-timer-throttling');
|
|
|
|
}
|
|
|
|
|
2017-03-28 20:08:14 +02:00
|
|
|
function readAppSettings() {
|
2017-03-28 18:19:39 +02:00
|
|
|
try {
|
2017-03-28 20:08:14 +02:00
|
|
|
return JSON.parse(fs.readFileSync(appSettingsFileName, 'utf8'));
|
2017-03-28 18:19:39 +02:00
|
|
|
} catch (e) {
|
2017-03-28 20:08:14 +02:00
|
|
|
return null;
|
2017-03-28 18:19:39 +02:00
|
|
|
}
|
2017-03-28 20:08:14 +02:00
|
|
|
}
|
|
|
|
|
2015-12-02 22:41:53 +01:00
|
|
|
function createMainWindow() {
|
2017-03-28 20:08:14 +02:00
|
|
|
const appSettings = readAppSettings();
|
2016-05-13 14:07:46 +02:00
|
|
|
mainWindow = new electron.BrowserWindow({
|
2015-12-02 22:41:53 +01:00
|
|
|
show: false,
|
2016-07-24 23:53:51 +02:00
|
|
|
width: 1000, height: 700, minWidth: 700, minHeight: 400,
|
2016-07-24 22:58:21 +02:00
|
|
|
icon: path.join(__dirname, 'icon.png'),
|
2017-03-28 20:08:14 +02:00
|
|
|
titleBarStyle: appSettings ? appSettings.titlebarStyle : undefined,
|
2016-07-24 22:58:21 +02:00
|
|
|
webPreferences: {
|
|
|
|
backgroundThrottling: false
|
|
|
|
}
|
2015-12-02 22:41:53 +01:00
|
|
|
});
|
|
|
|
setMenu();
|
2016-08-13 16:28:06 +02:00
|
|
|
mainWindow.loadURL(htmlPath);
|
2016-07-17 13:30:38 +02:00
|
|
|
mainWindow.webContents.on('dom-ready', () => {
|
|
|
|
setTimeout(() => {
|
2015-12-02 22:41:53 +01:00
|
|
|
mainWindow.show();
|
|
|
|
ready = true;
|
|
|
|
notifyOpenFile();
|
|
|
|
}, 50);
|
|
|
|
});
|
2017-01-29 11:53:09 +01:00
|
|
|
mainWindow.webContents.on('context-menu', onContextMenu);
|
2016-02-14 14:17:17 +01:00
|
|
|
mainWindow.on('resize', delaySaveMainWindowPosition);
|
|
|
|
mainWindow.on('move', delaySaveMainWindowPosition);
|
|
|
|
mainWindow.on('close', updateMainWindowPositionIfPending);
|
2016-07-24 23:10:03 +02:00
|
|
|
mainWindow.on('blur', mainWindowBlur);
|
2016-07-17 13:30:38 +02:00
|
|
|
mainWindow.on('closed', () => {
|
2015-12-02 22:41:53 +01:00
|
|
|
mainWindow = null;
|
2016-02-14 14:17:17 +01:00
|
|
|
saveMainWindowPosition();
|
2015-12-02 22:41:53 +01:00
|
|
|
});
|
2016-07-17 13:30:38 +02:00
|
|
|
mainWindow.on('minimize', () => {
|
2015-12-02 22:41:53 +01:00
|
|
|
emitBackboneEvent('launcher-minimize');
|
|
|
|
});
|
2017-03-26 15:24:14 +02:00
|
|
|
mainWindow.on('leave-full-screen', () => {
|
|
|
|
emitBackboneEvent('leave-full-screen');
|
|
|
|
});
|
|
|
|
mainWindow.on('enter-full-screen', () => {
|
|
|
|
emitBackboneEvent('enter-full-screen');
|
|
|
|
});
|
2017-06-02 20:04:57 +02:00
|
|
|
mainWindow.on('session-end', () => {
|
|
|
|
emitBackboneEvent('os-lock');
|
|
|
|
});
|
2016-02-14 14:17:17 +01:00
|
|
|
restoreMainWindowPosition();
|
2015-12-02 22:41:53 +01:00
|
|
|
}
|
|
|
|
|
2015-11-22 19:18:54 +01:00
|
|
|
function restoreMainWindow() {
|
2016-04-15 22:40:55 +02:00
|
|
|
if (mainWindow.isMinimized()) {
|
|
|
|
mainWindow.restore();
|
|
|
|
}
|
2015-11-22 19:18:54 +01:00
|
|
|
mainWindow.setSkipTaskbar(false);
|
2016-04-15 22:40:55 +02:00
|
|
|
mainWindow.focus();
|
2017-06-05 14:15:22 +02:00
|
|
|
setTimeout(destroyAppIcon, 0);
|
2015-11-22 19:18:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
function closeMainWindow() {
|
2016-04-15 22:40:55 +02:00
|
|
|
destroyAppIcon();
|
2015-12-02 22:41:53 +01:00
|
|
|
emitBackboneEvent('launcher-exit-request');
|
|
|
|
}
|
|
|
|
|
2016-04-15 22:40:55 +02:00
|
|
|
function destroyAppIcon() {
|
|
|
|
if (appIcon) {
|
|
|
|
appIcon.destroy();
|
|
|
|
appIcon = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-14 14:17:17 +01:00
|
|
|
function delaySaveMainWindowPosition() {
|
|
|
|
if (updateMainWindowPositionTimeout) {
|
|
|
|
clearTimeout(updateMainWindowPositionTimeout);
|
|
|
|
}
|
|
|
|
updateMainWindowPositionTimeout = setTimeout(updateMainWindowPosition, 500);
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateMainWindowPositionIfPending() {
|
|
|
|
if (updateMainWindowPositionTimeout) {
|
|
|
|
clearTimeout(updateMainWindowPositionTimeout);
|
|
|
|
updateMainWindowPosition();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateMainWindowPosition() {
|
|
|
|
if (!mainWindow) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
updateMainWindowPositionTimeout = null;
|
2017-01-31 07:50:28 +01:00
|
|
|
const bounds = mainWindow.getBounds();
|
2016-02-14 14:17:17 +01:00
|
|
|
if (!mainWindow.isMaximized() && !mainWindow.isMinimized() && !mainWindow.isFullScreen()) {
|
|
|
|
mainWindowPosition.x = bounds.x;
|
|
|
|
mainWindowPosition.y = bounds.y;
|
|
|
|
mainWindowPosition.width = bounds.width;
|
|
|
|
mainWindowPosition.height = bounds.height;
|
|
|
|
}
|
|
|
|
mainWindowPosition.maximized = mainWindow.isMaximized();
|
|
|
|
mainWindowPosition.fullScreen = mainWindow.isFullScreen();
|
2016-05-13 14:07:46 +02:00
|
|
|
mainWindowPosition.displayBounds = require('electron').screen.getDisplayMatching(bounds).bounds;
|
2016-02-14 14:17:17 +01:00
|
|
|
mainWindowPosition.changed = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
function saveMainWindowPosition() {
|
|
|
|
if (!mainWindowPosition.changed) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
delete mainWindowPosition.changed;
|
|
|
|
try {
|
2016-04-06 23:00:21 +02:00
|
|
|
fs.writeFileSync(windowPositionFileName, JSON.stringify(mainWindowPosition), 'utf8');
|
2016-02-14 14:17:17 +01:00
|
|
|
} catch (e) {}
|
|
|
|
}
|
|
|
|
|
|
|
|
function restoreMainWindowPosition() {
|
2016-07-17 13:30:38 +02:00
|
|
|
fs.readFile(windowPositionFileName, 'utf8', (e, data) => {
|
2016-02-14 14:17:17 +01:00
|
|
|
if (data) {
|
|
|
|
mainWindowPosition = JSON.parse(data);
|
2016-02-14 14:24:49 +01:00
|
|
|
if (mainWindow && mainWindowPosition) {
|
2016-02-14 14:17:17 +01:00
|
|
|
if (mainWindowPosition.width && mainWindowPosition.height) {
|
2017-01-31 07:50:28 +01:00
|
|
|
const displayBounds = require('electron').screen.getDisplayMatching(mainWindowPosition).bounds;
|
|
|
|
const db = mainWindowPosition.displayBounds;
|
2016-02-14 14:17:17 +01:00
|
|
|
if (displayBounds.x === db.x && displayBounds.y === db.y &&
|
|
|
|
displayBounds.width === db.width && displayBounds.height === db.height) {
|
|
|
|
mainWindow.setBounds(mainWindowPosition);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (mainWindowPosition.maximized) { mainWindow.maximize(); }
|
|
|
|
if (mainWindowPosition.fullScreen) { mainWindow.setFullScreen(true); }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-07-24 23:10:03 +02:00
|
|
|
function mainWindowBlur() {
|
|
|
|
emitBackboneEvent('main-window-blur');
|
|
|
|
}
|
|
|
|
|
2016-07-24 19:11:25 +02:00
|
|
|
function emitBackboneEvent(e, arg) {
|
2016-09-07 18:48:08 +02:00
|
|
|
if (mainWindow && mainWindow.webContents) {
|
|
|
|
arg = JSON.stringify(arg);
|
|
|
|
mainWindow.webContents.executeJavaScript(`Backbone.trigger('${e}', ${arg});`);
|
|
|
|
}
|
2015-11-22 19:18:54 +01:00
|
|
|
}
|
|
|
|
|
2015-11-02 18:35:56 +01:00
|
|
|
function setMenu() {
|
|
|
|
if (process.platform === 'darwin') {
|
2017-01-31 07:50:28 +01:00
|
|
|
const name = require('electron').app.getName();
|
|
|
|
const template = [
|
2015-11-02 18:35:56 +01:00
|
|
|
{
|
|
|
|
label: name,
|
|
|
|
submenu: [
|
2017-01-29 11:53:09 +01:00
|
|
|
{ role: 'about' },
|
2015-11-02 18:35:56 +01:00
|
|
|
{ type: 'separator' },
|
2017-01-29 11:53:09 +01:00
|
|
|
{ role: 'services', submenu: [] },
|
2015-11-02 18:35:56 +01:00
|
|
|
{ type: 'separator' },
|
2017-01-29 11:53:09 +01:00
|
|
|
{ accelerator: 'Command+H', role: 'hide' },
|
|
|
|
{ accelerator: 'Command+Shift+H', role: 'hideothers' },
|
|
|
|
{ role: 'unhide' },
|
2015-11-02 18:35:56 +01:00
|
|
|
{ type: 'separator' },
|
2017-01-29 11:53:09 +01:00
|
|
|
{ role: 'quit', accelerator: 'Command+Q' }
|
2015-11-02 18:35:56 +01:00
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: 'Edit',
|
|
|
|
submenu: [
|
2017-01-29 11:53:09 +01:00
|
|
|
{ accelerator: 'CmdOrCtrl+Z', role: 'undo' },
|
|
|
|
{ accelerator: 'Shift+CmdOrCtrl+Z', role: 'redo' },
|
2015-11-02 18:35:56 +01:00
|
|
|
{ type: 'separator' },
|
2017-01-29 11:53:09 +01:00
|
|
|
{ accelerator: 'CmdOrCtrl+X', role: 'cut' },
|
|
|
|
{ accelerator: 'CmdOrCtrl+C', role: 'copy' },
|
|
|
|
{ accelerator: 'CmdOrCtrl+V', role: 'paste' },
|
|
|
|
{ accelerator: 'CmdOrCtrl+A', role: 'selectall' }
|
2015-11-02 18:35:56 +01:00
|
|
|
]
|
2017-01-29 11:29:21 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
label: 'Window',
|
|
|
|
submenu: [
|
2017-01-29 11:53:09 +01:00
|
|
|
{ accelerator: 'CmdOrCtrl+M', role: 'minimize' }
|
2017-01-29 11:29:21 +01:00
|
|
|
]
|
2015-11-02 18:35:56 +01:00
|
|
|
}
|
|
|
|
];
|
2017-01-31 07:50:28 +01:00
|
|
|
const menu = electron.Menu.buildFromTemplate(template);
|
2016-05-13 14:07:46 +02:00
|
|
|
electron.Menu.setApplicationMenu(menu);
|
2015-11-02 18:35:56 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-29 11:53:09 +01:00
|
|
|
function onContextMenu(e, props) {
|
|
|
|
if (props.inputFieldType !== 'plainText' || !props.isEditable) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const Menu = electron.Menu;
|
|
|
|
const inputMenu = Menu.buildFromTemplate([
|
|
|
|
{role: 'undo'},
|
|
|
|
{role: 'redo'},
|
|
|
|
{type: 'separator'},
|
|
|
|
{role: 'cut'},
|
|
|
|
{role: 'copy'},
|
|
|
|
{role: 'paste'},
|
|
|
|
{type: 'separator'},
|
|
|
|
{role: 'selectall'}
|
|
|
|
]);
|
|
|
|
inputMenu.popup(mainWindow);
|
|
|
|
}
|
|
|
|
|
2015-10-24 21:06:44 +02:00
|
|
|
function notifyOpenFile() {
|
|
|
|
if (ready && openFile && mainWindow) {
|
2015-10-25 08:53:05 +01:00
|
|
|
openFile = openFile.replace(/"/g, '\\"').replace(/\\/g, '\\\\');
|
2015-10-24 21:06:44 +02:00
|
|
|
mainWindow.webContents.executeJavaScript('if (window.launcherOpen) { window.launcherOpen("' + openFile + '"); } ' +
|
|
|
|
' else { window.launcherOpenedFile="' + openFile + '"; }');
|
2015-10-25 08:53:05 +01:00
|
|
|
openFile = null;
|
2015-10-24 21:06:44 +02:00
|
|
|
}
|
|
|
|
}
|
2016-02-28 10:34:14 +01:00
|
|
|
|
2016-02-28 18:13:23 +01:00
|
|
|
function setGlobalShortcuts() {
|
2017-01-31 07:50:28 +01:00
|
|
|
const shortcutModifiers = process.platform === 'darwin' ? 'Ctrl+Alt+' : 'Shift+Alt+';
|
|
|
|
const shortcuts = {
|
2016-02-28 18:13:23 +01:00
|
|
|
C: 'copy-password',
|
|
|
|
B: 'copy-user',
|
2016-04-10 09:02:32 +02:00
|
|
|
U: 'copy-url',
|
|
|
|
T: 'auto-type'
|
2016-02-28 18:13:23 +01:00
|
|
|
};
|
2016-07-17 13:30:38 +02:00
|
|
|
Object.keys(shortcuts).forEach(key => {
|
2017-01-31 07:50:28 +01:00
|
|
|
const shortcut = shortcutModifiers + key;
|
|
|
|
const eventName = shortcuts[key];
|
2016-02-28 18:13:23 +01:00
|
|
|
try {
|
2016-07-17 13:30:38 +02:00
|
|
|
electron.globalShortcut.register(shortcut, () => {
|
2016-02-28 18:13:23 +01:00
|
|
|
emitBackboneEvent(eventName);
|
|
|
|
});
|
|
|
|
} catch (e) {}
|
2016-02-28 10:34:14 +01:00
|
|
|
});
|
|
|
|
}
|
2016-08-20 09:04:30 +02:00
|
|
|
|
|
|
|
function subscribePowerEvents() {
|
|
|
|
electron.powerMonitor.on('suspend', () => {
|
|
|
|
emitBackboneEvent('power-monitor-suspend');
|
|
|
|
});
|
|
|
|
electron.powerMonitor.on('resume', () => {
|
|
|
|
emitBackboneEvent('power-monitor-resume');
|
|
|
|
});
|
|
|
|
}
|
2017-06-05 13:33:12 +02:00
|
|
|
|
|
|
|
function deleteOldTempFiles() {
|
|
|
|
setTimeout(() => {
|
|
|
|
for (const dir of fs.readdirSync(tempUserDataPath)) {
|
|
|
|
if (dir !== tempUserDataPathRand) {
|
|
|
|
try {
|
|
|
|
deleteRecursive(path.join(tempUserDataPath, dir));
|
|
|
|
} catch (e) {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, 1000);
|
|
|
|
}
|
|
|
|
|
|
|
|
function deleteRecursive(dir) {
|
|
|
|
for (const file of fs.readdirSync(dir)) {
|
|
|
|
const filePath = path.join(dir, file);
|
|
|
|
if (fs.lstatSync(filePath).isDirectory()) {
|
|
|
|
deleteRecursive(filePath);
|
|
|
|
} else {
|
|
|
|
fs.unlinkSync(filePath);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fs.rmdirSync(dir);
|
|
|
|
}
|