So I started learning electron and I made a cool app. I wanted to add more things, but I'm kinda stuck. I tried to add cpu name using manufacturer(); in systeminformation. I have no idea what have I done wrong. I'll remind you last time that I'm a complete beginner, so expect me to be kinda dumb.
This is how my div I want to make looks like
<div class="cpu_name">
CPU name: <span id="cpu-name">-</span>
</div>
<div class="cpu_cores">
CPU cores: <span id="cpu-cores">-</span>
</div>
<div class="cpu_temperature">
CPU temperature: <span id="cpu-temperature">-</span>
</div>
</div>
Also, I didn't forget to use script
<script src="./renderer.js" defer type="module"></script>
<script src="./js/window.js" defer type="module"></script>
Next thing I've done was adding it into the renderer.js
I created var linked to the #cpu-name
const CPU_NAME = document.getElementById("cpu-name");
Then I created getCpuName() function
async function getCpuName(){
const name = await app.cpuName();
const cpu_name = name.manufacturer;
updateCpuName(cpu_name);
}
Since I called a updateCpuName() method, I created one
function updateCpuName(cpu__name){
CPU_NAME.innerText = cpu__name;
console.log(cpu__name);
}
Next thing I've done was adding it into the preload.js. I've done the preload.js by tutorial, because I don't really uderstand everything there yet...
This is my whole preload.js script
const os = require("os");
const { ipcRenderer, contextBridge } = require("electron");
const API = {
window:{
close: () => ipcRenderer.send("app/close"),
minimize: () => ipcRenderer.send("app/minimize"),
},
cpuUsage: (data) => ipcRenderer.invoke("cpu/get", data),
cpuName: (data) => ipcRenderer.invoke("cpu/name", data),
}
contextBridge.exposeInMainWorld("app", API);
But this cpuName: (data) => ipcRenderer.invoke("cpu/name", data), is the only important thing here.
Last thing I did was adding it into the index.js.
So I basically created const with systeminformation
const {currentLoad, manufacturer, cpu } = require("systeminformation");
and then made a ipcMain.handle();
ipcMain.handle("cpu/name", async (_, data) => {
const name = await manufacturer();
return name;
});
I know for sure I've done something wrong, but I'm not able to figure out what. It's just fun project I'm working on to get better in it. Thank you for even just reading this ^-^
There is an npm module called systeminformation (https://www.npmjs.com/package/systeminformation) that will help you with this. I have never accessed the CPU using electron or this module, but the module seems very easy to use and is very well documented.
Please also look at the "Known Issues" section of the module readme. Some additional dependencies are required for checking CPU temperature, so you might want to look at that too!
Related
I want render a Blazor component from javascript.
See https://devblogs.microsoft.com/aspnet/asp-net-core-updates-in-net-6-rc-1/ "Render Blazor components from JavaScript"
I have a HTML file:
<script src="/_framework/blazor.server.js"></script>
<div id="counter"></div>
<script>
async function ready() {
let containerElement = document.getElementById('counter');
await Blazor.rootComponents.add(containerElement, 'counter', { incrementAmount: 10 });
}
document.addEventListener("DOMContentLoaded", ready);
</script>
And do
builder.Services.AddServerSideBlazor(options =>
{
options.RootComponents.RegisterForJavaScript<Counter>("counter");
});
Error message (JavaScript):
test.html:14 Uncaught (in promise) Error: Dynamic root components have not been enabled in this application.
at E (blazor.server.js:1)
at Object.add (blazor.server.js:1)
at HTMLDocument.ready (test.html:8)
How can i enable dynamic root components?
The error is happening because of the delay between the document loading and Blazor being "ready" to process your request to add a component.
There doesn't appear to be any official documented solution for this, but something like this is possible
Change Blazor to manual start:
index.html
<script src="_framework/blazor.webassembly.js" autostart="false"></script>
<script src="js/script.js" defer></script>
Start Blazor, then try to add components - repeat until success
script.js
document.addEventListener("DOMContentLoaded", startBlazor)
function startBlazor() {
Blazor
.start()
.then(() => {
requestAnimationFrame(AddBlazorComponents)
})
.catch((error) => console.error(error));
}
let attempts = 0
const maxAttempts = 120 // arbitrary choice
function AddBlazorComponents() {
if (attempts > maxAttempts) {
// give up after trying too many times
console.error("Could not load Blazor components.")
return
}
const containerElement = document.querySelector('#app')
Blazor.rootComponents.add(containerElement, 'app', {})
.catch(reason => {
if (reason.message === "Dynamic root components have not been enabled in this application.")
requestAnimationFrame(AddBlazorComponents)
else
console.error(reason)
})
attempts++
}
Better solution?
You could, possibly, add a JSInterop call to your .NET code to facilitate this, after the application starts - but it would be different for the different hosting models : Blazor Server / Blazor WebAssembly.
Even better might be to make a PR to the aspnetcore repo with a method of pre-registering components for the framework to load when it is ready.
Update 29 sep 2021
It turns out there is some currently undocumented functionality that helps here. I have copied my comment from #Swimburger 's github issue below:
The new JavaScript Initializer afterStarted is called (potentially) too soon for Dynamic components, but it turns out you can pass an initializer to be called for a dynamic component - but it is not well documented or intuitive imho.
To make this work, I changed the app startup like this (adding javaScriptInitializer: "loadApp") in program.cs:
builder.RootComponents.RegisterForJavaScript<App>(identifier: "app", javaScriptInitializer: "loadApp");
Then, and this is where it was non-intuitive, I added an export to my module (like afterStarted) called loadApp - but it wasn't called.
It turned out that loadApp is only found if I add it to the window object, so I added this to index.html
<script src="_framework/blazor.webassembly.js"></script>
<script>
window.loadApp = function (component,params) {
let containerElement = document.querySelector('#app')
window.Blazor.rootComponents.add(containerElement, component, params)
}
</script>
This feels like I am missing something in how to export my custom initializer OR it really does need to be added to window directly, which seems odd...
I'm in the process of splitting my cloud functions in separate files, so as to lazy load some dependencies and reduce cold start.
Basically, I'm trying to replicate Doug's answer from here, but not having to create a separate file for each function.
In my index.js file:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.createStripeCustomer = functions.auth.user().onCreate(async (user) => {
const { createUser } = require('./user/main');
await createUser(user, admin);
});
And in my 'user/main.js' file:
const functions = require('firebase-functions');
const { Stripe } = require('stripe');
const stripe = new Stripe(functions.config().stripe.secret);
const endpointSecret = functions.config().stripe.singing;
const createStripeCustomer = async (user, admin) => {
// Do some stuff
};
module.exports = { createUser: createStripeCustomer };
The intention behind this split, is that I have some functions which require Stripe, and some which do not, hence I don't want them all to load it unnecessarily, but I get an error- "missing ) after argument list".
Any suggestions as to what has gone wrong?
Your solution does not look like the real solution...
Maybe you also fixed something which looked insignificant... Like an extra double quote.
let something = "A"
console.log("hello", something")
See the extra double quote after the variable?
It produces the same error you mentionned.
It is a common error due to code editors just adding thing for you... And if you are like me and look at the keyboard instead of the screen, it is easy not to notice.
Just in case anyone experiences a similar problem, I managed to get it working, simply by changing:
const createStripeCustomer = async (user, admin) => {
// Do some stuff
};
to:
async function createStripeCustomer(user, admin) {
// Do some stuff
};
I have no idea why that was an issue, but it seemed to resolve the problem I had before.
I'm composing a small library (for the first time) that utilizes the Resize Observer API. However, I want to include the polyfill for browsers that don't support it, and only load it into the final build if it's required, though I'm not sure where to place the API check/module load, or if this method is the right way to do it.
Say I have my index file which is the main file exported with the library, should I add this statement outside the function definition like so:
// index.js
(async () => {
if (!ResizeObserver in window) {
const module = await import('resize-observer-polyfill');
window.ResizeObserver = module.ResizeObserver
}
}();
// main function exported
export default () => {
function loadResizeObserver() {
const ro = new ResizeObserver((entries) => {
// ...
})
}
}
Is there a better way to compose something like this? How will this affect the build and what's included if a consumer installs the package (with and without native RO support?)
First time publishing and realized I know nothing about it.
Firstly: as far as I can tell, this is not a duplicate. The other questions with similar problems are all slightly different, e.g. use a transformation like babel or have problems with transitive imports. In my case I have no transformation, I have one test file and one file imported file that will be tested. I just started using jest and use the default setting, so there is no configuration file to post.
When I try to run my tests I get the error message:
Test suite failed to run
Jest encountered an unexpected token
This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.
The tested file:
export function showTooltip(x, y, content) {
const infoElement = document.getElementById('info');
infoElement.style.left = `${x}px`;
infoElement.style.top = `${y}px`;
infoElement.style.display = 'block';
infoElement.innerText = createTooltipText(content);
}
function createTooltipText(object) {
return Object.keys(object)
.filter(key => key != 'id')
.map(key => `${key} : ${object[key]}`)
.join('\n');
}
export function hideTooltip() {
const infoElement = document.getElementById('info');
infoElement.style.display = 'none';
}
The test:
import {showTooltip, hideTooltip} from '../../../src/public/javascripts/tooltip.js';
const TOOLTIP_DUMMY = {
style: {
left: 0,
top: 0,
display: '',
innerText: ''
}
};
test('showTooltip accesses the element with the id \'info\'', () => {
const getElementByIdMock = jest.fn(() => TOOLTIP_DUMMY);
document.getElementById = getElementByIdMock;
showTooltip(0, 0, {});
expect(getElementByIdMock).toHaveBeenCalledWith('info');
});
test('hideTooltip accesses the element with the id \'info\'', () => {
const getElementByIdMock = jest.fn(() => TOOLTIP_DUMMY);
document.getElementById = getElementByIdMock;
hideTooltip();
expect(getElementByIdMock).toHaveBeenCalledWith('info');
});
As you can see I am using plain javascript so I am not sure what to do here. The error message gives further hints about Babel which does not really apply to my case.
Sidenote: My test might be flawed. I am currently trying to figure out how to use mocks to avoid interaction with the document and I am not sure if that is the way. However this is not the point of this question as it should not affect the ability of the tests to run, but I am very open for suggestions.
EDIT: Why this is not a duplicate to this question: It kind of is, but I feel that question and the accepted answer were not really helpful for me and hopefully someone will profit from this one.
I have found the solution to my problem:
As suggested in this answer, you need to use Babel. This can be done as suggested here, but I used #babel/env-preset as it is suggested on the Babel website.
This left me with the problem that jest internally uses babel-core#6.26.3, but at least babel 7 was required. This problem is described here. I used the temporary fix of manually copying and overwriting babel-core from my node-modules directory to the node-modules directories of jest-config and jest-runtime. This dirty fix is also described in the previous link.
I have yet to find a clean solution, but at least this works.
Use global.document.getElementById = getElementByIdMock; In some configurations Jest doesn't have access to document object directly.
I would like to make use of a function called executeJavaScript() from the Electron webContents API. Since it is very close to eval() I will use this in the example.
The problem:
I have a decent sized script but it is contained in a template string.
Expanding this app, the script could grow a lot as a string.
I am not sure what the best practices are for this.
I also understand that eval() is dangerous, but I am interested in the principal of my question.
Basic eval example for my question:
// Modules
const fs = require('fs');
// CONSTANTS
const EXAMPLE_1 = 'EXAMPLE_1';
const EXAMPLE_2 = 'EXAMPLE_2';
const EXAMPLE_3 = 'EXAMPLE_3';
const exampleScriptFunction = require('./exampleScriptFunction');
const exampleScriptFile = fs.readFileSync('./exampleScriptFile.js');
// using direct template string
eval(`console.log(${EXAMPLE_1})`);
// using a method from but this doesnt solve the neatness issue.
eval(exampleScriptFunction(EXAMPLE_2));
// What I want is to just use a JS file because it is neater.
eval(`${exampleScriptFile}`);
exampleScriptFunction.js
module.exports = function(fetchType) {
return `console.log(${fetchType});`;
}
This will allow me to separate the script to a new file
what if I have many more then 1 variable???
exampleScriptFile.js:
console.log(${EXAMPLE_3});
This clearly does not work, but I am just trying to show my thinking.
back ticks are not present, fs loads as string, main file has back ticks.
This does not work. I do not know how else to show what I mean.
Because I am loading this will readFileSync, I figured the es6 template string would work.
This allows me to write a plain js file with proper syntax highlighting
The issue is the variables are on the page running the eval().
Perhaps I am completely wrong here and looking at this the wrong way. I am open to suggestions. Please do not mark me minus 1 because of my infancy in programming. I really do not know how else to ask this question. Thank you.
Assuming your source is stored in exampleScriptFile:
// polyfill
const fs = { readFileSync() { return 'console.log(`${EXAMPLE_3}`);'; } };
// CONSTANTS
const EXAMPLE_1 = 'EXAMPLE_1';
const EXAMPLE_2 = 'EXAMPLE_2';
const EXAMPLE_3 = 'EXAMPLE_3';
const exampleScriptFile = fs.readFileSync('./exampleScriptFile.js');
// What I want is to just use a JS file because it is neater.
eval(exampleScriptFile);
Update
Perhaps I wasn't clear. The ./exampleScriptFile.js should be:
console.log(`${EXAMPLE_3}`);
While what you're describing can be done with eval as #PatrickRoberts demonstrates, that doesn't extend to executeJavaScript.
The former runs in the caller's context, while the latter triggers an IPC call to another process with the contents of the code. Presumably this process doesn't have any information on the caller's context, and therefore, the template strings can't be populated with variables defined in this context.
Relevant snippets from electron/lib/browsers/api/web-contents.js:
WebContents.prototype.send = function (channel, ...args) {
// ...
return this._send(false, channel, args)
}
// ...
WebContents.prototype.executeJavaScript = function (code, hasUserGesture, callback) {
// ...
return asyncWebFrameMethods.call(this, requestId, 'executeJavaScript',
// ...
}
// ...
const asyncWebFrameMethods = function (requestId, method, callback, ...args) {
return new Promise((resolve, reject) => {
this.send('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', requestId, method, args)
// ...
})
}
Relevant snippets from electron/atom/browser/api/atom_api_web_contents.cc
//...
void WebContents::BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::FunctionTemplate> prototype) {
prototype->SetClassName(mate::StringToV8(isolate, "WebContents"));
mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
// ...
.SetMethod("_send", &WebContents::SendIPCMessage)
// ...
}