docs.sheetjs.com/docz/static/electron/main.js

120 lines
5.5 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* sheetjs (C) 2013-present SheetJS -- https://sheetjs.com */
const { app, BrowserWindow, ipcMain, dialog } = require('electron');
const path = require('path');
const XLSX = require('xlsx');
const EXT_REGEX = /\.(xls|xlsx|xlsm|xlsb|xml|csv|txt|dif|sylk|slk|prn|ods|fods|htm|html|numbers)$/i;
const pendingPaths = []; // paths of files that were opened before the window was created. These are queued and loaded once the window is created.
let win = null; // reference to the main window, to make sure we don't create multiple windows.
/* In electron, the main process is the only process that can directly interface with the operating system.
The renderer process is sandboxed and cannot run any non-browser code.
To allow the renderer process to interface with the operating system, we use the contextBridge API to expose the API to the renderer process.
https://www.electronjs.org/docs/latest/api/context-bridge
*/
/* ----------------------------------------------------------------------------- */
/* IPC handlers that allow communication between main and renderer processes */
/* ----------------------------------------------------------------------------- */
/* These three functions can be used to interface with the operating system from the renderer process.
the ipcMain.handle() function is used to register a handler for a specific event.
when the renderer process calls the corresponding function, the main process will receive the event and execute the handler.
In this case, we are listening to events which allow the renderer process to open/save file dialogs and show modal messages.
*/
ipcMain.handle('dialog:openFile', (_e, filters) =>
dialog.showOpenDialog({ title: 'Select a file', filters, properties: ['openFile'] })
);
ipcMain.handle('dialog:saveFile', (_e, filters) =>
dialog.showSaveDialog({ title: 'Save file as', filters })
);
ipcMain.handle('dialog:message', (_e, msg) =>
dialog.showMessageBox({ message: msg, buttons: ['OK'] })
);
/* ----------------------------------------------------------------------------- */
/* Utility functions */
/* ----------------------------------------------------------------------------- */
function sendToRenderer(fp) {
if (win && win.webContents) win.webContents.send('file-opened', fp);
else pendingPaths.push(fp);
}
/*
On Windows and Linux, opening a file using the "open with" menu option or `open` command will pass the file path as a startup argument to the app.
We need to parse it, and test if it is a spreadsheet file.
*/
function firstSpreadsheetFromArgv() {
const args = process.defaultApp ? process.argv.slice(2) : process.argv.slice(1);
return args.find((a) => EXT_REGEX.test(a));
}
/* ----------------------------------------------------------------------------- */
/* Single-instance guard */
/* ----------------------------------------------------------------------------- */
// Windows and Linux only: If the app is already running, we need to prevent a new instance from launching when opening a file via the "open with" menu option or `open` command.
if (!app.requestSingleInstanceLock()) app.quit();
else {
app.on('second-instance', (_e, argv) => {
const fp = argv.find((a) => EXT_REGEX.test(a));
if (fp) sendToRenderer(fp);
if (win) { win.show(); win.focus(); }
});
}
// macOS file / url events
app.on('open-file', (evt, fp) => { evt.preventDefault(); sendToRenderer(fp); });
app.on('open-url', (evt, url) => { evt.preventDefault(); sendToRenderer(url.replace('file://', '')); });
/* ----------------------------------------------------------------------------- */
/* Create the window */
/* ----------------------------------------------------------------------------- */
function createWindow() {
if (win) return;
win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, './preload.js'), // preload script that will be executed in the renderer process before the page is loaded and act as a bridge between the main and renderer processes within a worker thread.
contextIsolation: true, // isolate and enable bridge, keeping the renderer process sandboxed and separated from the main process.
nodeIntegration: false, // no Node.js in renderer process.
nodeIntegrationInWorker: true, // enable Node.js in worker threads.
worldSafeExecuteJavaScript: true
}
});
win.loadFile('index.html');
if (process.env.NODE_ENV === 'development') win.webContents.openDevTools();
win.on('closed', () => { win = null; });
win.webContents.once('did-finish-load', () => {
pendingPaths.splice(0).forEach(sendToRenderer);
});
}
/* ----------------------------------------------------------------------------- */
/* App lifecycle */
/* ----------------------------------------------------------------------------- */
app.whenReady().then(() => {
const fp = firstSpreadsheetFromArgv();
if (fp) pendingPaths.push(fp);
createWindow();
});
if (app.setAboutPanelOptions) {
app.setAboutPanelOptions({
applicationName: 'sheetjs-electron',
applicationVersion: `XLSX ${XLSX.version}`,
copyright: '(C) 2017present SheetJS LLC'
});
}
app.on('activate', createWindow);
app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit(); });