This is the project structure I would like to have.
kareljs
folder is an Electron app and if I run
npm start
inside that folder, a window pop up and run
method of karel.js is executed when the button Run Karel
is clicked.
./eg01/hello_karel.js
, ./eg02/pick_newspaper.js
etc., are where I program Karel using the methods provided by ./kareljs/karel/karel.js
module. Running the program must bring up the same window above, but when Run Karel
button clicked, the run
method in these Karel program files should be called, instead of the default one.
(What I need is sort of method overriding in Java. Here I override run
method via new js file).
And to run the program, I should just be able to
node ./eg01/hello_karel.js
Or
node hello_karel.js
if I am in eg01
folder. Ideally, no config file should be required to run these programs. I mean inside eg01
, eg02
folders. Of course, Karel is for newbies, and for them, the simpler the better it is.
What are the possible mechanisms to achieve my requirements?
Code:
// main.js
const { app, BrowserWindow } = require('electron/main')
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
contextIsolation: false,
nodeIntegration: true
},
})
win.loadFile('index.html')
}
app.whenReady().then(() => {
createWindow()
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
})
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<meta
http-equiv="X-Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<title>Hello from Electron renderer!</title>
</head>
<body>
<h1>Hello from Electron renderer ... !</h1>
<p>👋</p>
<p id="info"></p>
<p><button class="run">Run Karel</button></p>
<script src="./renderer.js"></script>
</body>
</html>
// karel.js
const move = () => {
alert("Karel moves one step forward!")
};
const turnLeft = () => {
alert("Kare turns left!")
};
const pickBeeper = () => {
alert("Karel picks a beeper!")
};
const putBeeper = () => {
alert("Karel puts a beeper!")
};
const run = () => {
alert("This is the default run method!")
}
// Updated: to write user_defined.js file with content from hello_karel.js
const { exec,spawn } = require("child_process");
const fs = require("fs");
var path = require('path');
const start_karel = (file_name) => {
console.log(__dirname)
console.log("The .js file executed: " + file_name)
var user_script;
fs.readFile(file_name, 'utf8', (err, data) => {
if (err) {
console.error(err);
return;
}
console.log(data);
var user_defined = path.join(__dirname, '..', 'user_defined.js');
var user_script = data.replace(/start_karel\(__filename\)/g, "")
fs.writeFile(user_defined, user_script, 'utf-8', function (err) {
console.log(err);
});
});
var index_html = path.join(__dirname, '..', 'index.html');
/*
fs.readFile(index_html, 'utf8', (err, data) => {
if (err) {
console.error(err);
return;
}
console.log("about to replace...")
console.log(data);
var replaced = data.replace(/default/g, 'user')
console.log("replaced " + replaced)
fs.writeFile(index_html, replaced, 'utf-8', function (err) {
console.log(err);
});
console.log("done replace...")
});
*/
exec("cd ../kareljs && ./node_modules/.bin/electron main.js " + file_name, (error, stdout, stderr) => {
if (error) {
console.log(`error: ${error.message}`);
return;
}
if (stderr) {
console.log(`stderr: ${stderr}`);
return;
}
console.log(`stdout: ${stdout}`);
});
/*
spawn("cd ../kareljs && ./node_modules/.bin/electron main.js " + file_name, {
stdio: 'inherit',
shell: true
})
*/
}
module.exports = {move, turnLeft, putBeeper, pickBeeper, run, start_karel};
//renderer.js
const {move, turnLeft, putBeeper, pickBeeper, run} = require('./karel/karel')
const btn = document.querySelector('.run')
btn.addEventListener('click', () => {run()})
package.json
{
"name": "hello-electron-app",
"version": "1.0.0",
"description": "test",
"main": "main.js",
"scripts": {
"start": "electron .",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"electron": "^28.2.1"
}
}
//hello_karel.js
const {move, turnLeft, putBeeper, pickBeeper, run, start_karel} = require('../kareljs/karel/karel')
function turnRight() {
turnLeft()
turnLeft()
turnLeft()
}
start_karel()
Also have GitHub repository here.
Update: I can now start the app just by running
node hello_karel.js
Still need to figure out how to change the behavior of the run
method which is called via the button click. (Please see the update in karel.js
above.)
hello_karel.js
above) to the app somehow, there should be a way to call therun
method in the file dynamically.node hello_karel.js
, the files contents can be read frommain.js
. After this, I should be able to share the content (ofhello_karel.js
in this case) to renderer process. Or may be I just extract the run and other user defined functions , and then write them toindex.html
, like<script>user defined scripts<\script>
karel.js
module, I just write thehello_karel.js
content to a new fileuser_defined.js
file, which is included inindex.html
file with<script src="./user_defined.js"> </script>
. I need to removestart_karel(__filename)
fromhello_karel.js
, otherwise, infinite Electron app start one after another without stopping (I was thinking only one more app will get started when the script is loaded inindex.html
). Don't know why. I don't know what problem the current method can have. But at least, it give the result I want.