How to use filesystem (fs) in angular-cli with electron-js - node.js

I have set up an angular-cli project
(# Angular / cli: 1.0.0-rc.2 node: 6.10.0 os: linux x64)
With electron js (v1.6.2)
And I need to use the filesystem to create / delete .csv files and folders, but I can not do includ in the angular component
How could you configure the angular-cli to be able to: import fs from 'fs'?

You wouldn't configure Angular-CLI to use the NodeJS fs module.
In electron you have 2 processes; main and renderer. The main process controls items such as the browserWindow, which is essentially the 'window' the user sees when they open their app, and in turn this loads the html file for the view. Here, in the main process, you import the fs module.
In the render process, you would handle actions from the view, and send them to the main process. This is where you would use IPC to communicate via events to do something with the main process. Once that event is triggered, the render process takes the event and sends it to main. Main would do something with it, and open a file for example on the desktop.
I would recommend using the electron API demo application to see clear examples of this. Here is an example of print to pdf using FS (from the demo app).
Also, here is an electron application github example written by Ray Villalobos using React, which has some similar concepts that will show you how to integrate components in your app.
Render process:
const ipc = require('electron').ipcRenderer
const printPDFBtn = document.getElementById('print-pdf')
printPDFBtn.addEventListener('click', function (event) {
ipc.send('print-to-pdf')
})
ipc.on('wrote-pdf', function (event, path) {
const message = `Wrote PDF to: ${path}`
document.getElementById('pdf-path').innerHTML = message
})
Main Process:
const fs = require('fs')
const os = require('os')
const path = require('path')
const electron = require('electron')
const BrowserWindow = electron.BrowserWindow
const ipc = electron.ipcMain
const shell = electron.shell
ipc.on('print-to-pdf', function (event) {
const pdfPath = path.join(os.tmpdir(), 'print.pdf')
const win = BrowserWindow.fromWebContents(event.sender)
// Use default printing options
win.webContents.printToPDF({}, function (error, data) {
if (error) throw error
fs.writeFile(pdfPath, data, function (error) {
if (error) {
throw error
}
shell.openExternal('file://' + pdfPath)
event.sender.send('wrote-pdf', pdfPath)
})
})
})

You can try using const fs = (<any>window).require("fs"); within the component or better still, create a service.ts provider to handle i/o operations.

Related

Vue3 Electron get user docs folder

How can i get the user docs folder path for an electron app running on the desktop.
I've tried the following but get an error that app is undefined.
import fs from "fs";
const { app } = require("electron");
export function getFilepath() {
const filepath = app.getPath("userData") + "/settings.json";
return filepath;
}
This code lives in a helpers.js file that is being import through my electron_preload.js file.
I have no clue what or how to solve this.
import { contextBridge, ipcRenderer } from "electron";
const helpers= require("../src/helpers");
contextBridge.exposeInMainWorld("helpers", helpers);
Since you're executing the code in your preload environment, it is being run in the renderer process of the corresponding BrowserWindow. However, app is limited to the main process' execution scope (source) which is why your code throws the error.
You will have to expose the path via IPC.
// Main process, app.js or whatever
const { ipcMain, app } = require ("electron");
ipcMain.handle ("get-user-data-path", (event, ...args) => {
return app.getPath ("userData") + "/settings.json";
});
// In helpers.js
import fs from "fs";
const { ipcRenderer } = require("electron");
export async function getFilepath () {
return await ipcRenderer.invoke ("get-user-data-path");
}
For a more in-depth explanation, see the official Electron IPC tutorial.
(As a side note: I/O to the filesystem should probably be done by the main process, not the renderer. Worth considering from a security point of view.)

Importing WASM files into Electron main process

I'm building an Electron app that needs to use Web-Assembly (WASM), however I'm hitting an issue with Fetch throwing a TypeError: Only absolute URLs are supported when importing my WASM file.
Also, perhaps this raises the broader question as to whether the Electron main process or the renderer process should be used to run the WASM ? It does seem to work in the render process.
Here's the complete error:
TypeError: Only absolute URLs are supported
at parseURL (/Users/devuser/development/electron-api-demos/node_modules/node-fetch/dist/index.cjs:897:8)
at new Request (/Users/devuser/development/electron-api-demos/node_modules/node-fetch/dist/index.cjs:922:17)
at /Users/devuser/development/electron-api-demos/node_modules/node-fetch/dist/index.cjs:1175:19
at new Promise (<anonymous>)
at fetch (/Users/devuser/development/electron-api-demos/node_modules/node-fetch/dist/index.cjs:1173:9)
at IpcMainImpl.<anonymous> (/Users/cbourne/development/electron-api-demos/main-process/communication/async-msg.js:20:36)
at IpcMainImpl.emit (events.js:223:5)
at WebContents.<anonymous> (electron/js2c/browser_init.js:4093:15)
at WebContents.emit (events.js:223:5)
And here's the main-process code I'm testing with:
const {ipcMain} = require('electron')
require('/Users/devuser/development/electron-api-demos/script/wasm_exec.js')
const fetch = require("node-fetch");
ipcMain.on('asynchronous-message', (event, arg) => {
if (!WebAssembly.instantiateStreaming) { // polyfill
WebAssembly.instantiateStreaming = async (resp, importObject) => {
const source = await (await resp).arrayBuffer();
return await WebAssembly.instantiate(source, importObject);
};
}
const go = new Go();
let mod, inst;
WebAssembly.instantiateStreaming(fetch("test.wasm"), go.importObject).then((result) => {
mod = result.module;
inst = result.instance;
document.getElementById("runButton").disabled = false;
}).catch((err) => {
console.error(err);
});
async function run() {
console.clear();
await go.run(inst);
inst = await WebAssembly.instantiate(mod, go.importObject); // reset instance
}
event.sender.send('asynchronous-reply', 'pong')
})
The problem is not WASM at all, but the request that is supposed to get the binary. Your fetch comes from node-fetch; the main process runs in Node.js and as such, does not have a base address like a normal page. Either provide a full file:/// absolute URL to fetch, or, more easily, use fs.readFileSync:
const fs = require('fs');
WebAssembly.instantiate(fs.readFileSync("text.wasm"));
I can only answer part of your question.
WASM should probably not be run in the Main process. Even though WASM will run in an independent thread, you should reduce the load on the Main process to the maximum extent possible. When the Main process is blocked, even something like minimizing your app will not occur until it becomes unblocked.
For more info, this is a good article: https://medium.com/actualbudget/the-horror-of-blocking-electrons-main-process-351bf11a763c
Have you tried changing fetch("test.wasm") to fetch("./test.wasm") or hardcoding a direct path to local file at least for dev purposes?

Nodejs/electron Override chromium browser timeout.

Been looking all over for this information, closest i can see is the
command line switches. i have a web page that i ported to electron and im trying to override the Browser inactivity timeout. for example running a report over 2 minutes the browser times out the connection. My thinking for a quick fix was to wrap in electron and extend the browser timeout.
https://peter.sh/experiments/chromium-command-line-switches/#timeout
https://github.com/electron/electron/blob/master/docs/api/chrome-command-line-switches.md
In Electron the main.js looks to have wrapped the modules in webpack
in all of the examples ive found
it looks like this
You can use app.commandLine.appendSwitch to append them in your app's main script before the ready event of the app module is emitted:
const { app } = require('electron')
app.commandLine.appendSwitch('remote-debugging-port', '8315')
app.commandLine.appendSwitch('host-rules', 'MAP * 127.0.0.1')
app.on('ready', () => {
// Your code here
})
however in my electron file my const app is actually
const path = __webpack_require__(14);
const electron = __webpack_require__(18);
const unusedFilename = __webpack_require__(20);
const pupa = __webpack_require__(23);
const extName = __webpack_require__(24);
const {app, shell} = electron;
i have tried
app.commandLine.appendSwitch("--disable-renderer-backgrounding");
app.commandLine.appendArgument('--disable-timeouts-for-profiling');
Doesnt seem to work, Any hints would be appreciated.

How can I get information about the file that launched my app?

Similar to How to get the arguments for opening file with electron app but the solution there is not working for me.
Using:
OS - Windows 10
Electron - https://github.com/castlabs/electron-releases.git#v1.8.7-vmp1010
electron-builde - v20.28.3
I have a an electron app build with electron-builder, and using the latter I have specified a custom file association, .custom.
So when you double-click on a file with this extension, file.custom, the installed app opens. This file would have some data in it that the app needs, and I'd like to read this data using my app.
Is there any way that my app can detect what launched it, so that I can say "file.custom" launched me, and it's sitting at "C:\Users\Owner\Downloads\,?
The file does not appear in process.argv
You can get a reference to the file using process.argv, example:
var ipc = require('ipc');
var fs = require('fs');
// read the file and send data to the render process
ipc.on('get-file-data', function(event) {
var data = null;
if (process.platform == 'win32' && process.argv.length >= 2) {
var openFilePath = process.argv[1];
data = fs.readFileSync(openFilePath, 'utf-8');
}
event.returnValue = data;
});
source: Source

Electron app - logging to file in production

I want to get logs if something wrong happens to my electron app when in production mode i.e after giving .exe file to a user wrt windows platform.
How to go about it, how can i basically write my errors to a file which will be in cyclic in nature.
Take a look at electron log
// Setup file logging
const log = require('electron-log');
log.transports.file.level = 'info';
log.transports.file.file = __dirname + 'log.log';
// Log a message
log.info('log message');
EDIT:
As mentioned in the comments the "log.transports.file.file" is deprecated.
Instead I would suggest to use the following method.
log.transports.file.resolvePath = () => __dirname + "/log.log";
Create a file next to you electron.js called logger.js
const path = require("path");
const log = require('electron-log');
log.transports.file.resolvePath = () => path.join(__dirname, '/logsmain.log');
log.transports.file.level = "info";
exports.log = (entry) => log.info(entry)
then on your app
const logger = require('electron').remote.require('./logger');
logger.log("some text")
Please have a look here:
https://www.electronjs.org/docs/api/net-log
const { netLog } = require('electron')
app.whenReady().then(async () => {
await netLog.startLogging('/path/to/net-log')
// After some network events
const path = await netLog.stopLogging()
console.log('Net-logs written to', path)
})

Resources