I am trying to use Robot.js inside of a React.js application. I have Robot.js installed and can run it to get the results in a separate file. I cannot find a way to run Robot.js inside a React component because I get an error "robot.getMousePos()" is not a function. All I really need is the value of "hex". Is there a way to export the variable into my react component, Or even better run the Robot.js code in the same component without getting errors?
This is the Robot.js example:
const color = () => {
// Get pixel color under the mouse.
var robot = require("robotjs");
// Get mouse position.
var mouse = robot.getMousePos();
// Get pixel color in hex format.
var hex = robot.getPixelColor(mouse.x, mouse.y);
console.log("#" + hex + " at x:" + mouse.x + " y:" + mouse.y);
};
color();
it returns the value of "hex" which is equal to the hexidecimal color under the mouse at the given x and y coords (#1e1e1e at x:746 y:511)
I am trying to get that output into my React component whose code is here:
import React from "react";
const Robot = () => {
return (
<div>
<h1>Robot</h1>
</div>
);
};
export default Robot;
Robot.js is a library for desktop automation in Node.js.
React is a library for doing DOM processing in a browser.
You can't run code that depends on Node.js in a browser.
If you want these two bits of code to work together then you'll need to write a web service that they can communicate through (or run your code with something like Electron and then use the IPC API to communicate between the main and renderer processes).
Indeed you will have to run it with some buddy as Electron.
I paste below a briefing about how you could reach it with Electron from here:
I was having a similar issue.
First I deleted entire node_modules directory and reinstalled them
again by running npm installin project directory (you have to define
all modules&versions in a package.json file for that).
Then removed robotjs from node_modules directory manually. And then
ran .\node_modules.bin\electron-rebuild.cmd.
After that I installed robotjs again by npm install robotjs.
Then cd .\node_modules\robotjs and node-gyp rebuild --runtime=electron
--target=1.3.3 --disturl=https://atom.io/download/atom-shell --abi=48 (1.3.3 is current version of electron)
and problem solved!
I had a similar issue, hopefully I was already using Electron and this worked fine.
Related
I am using the electron-react boilerplate and want to use an electron dialog in App.tsx:
const { dialog } = require('#electron/remote') //also tried with import
const Hello = () => {
const readFromFile = async () => {
dialog.showOpenDialog({})
}
return (
<>
<button onClick={() => readFromFile()} >Test</button>
</>
)
}
in main.ts I placed the following line at the top
require('#electron/remote/main').initialize()
In the end I always get this error:
Module not found: Error: Can't resolve 'fs' in 'C:\Users\myUsername\source\repos\electronTest\node_modules\electron'
I also tried nodeIntegration: true and contextIsolation: false
Just spent a while on this and <rant about JS community>... eh. Maybe this will help others.
tl;dr
You can only use electron's imports in electron-main side.
longer story
Electron is split into electron-main and electron-renderer. As others suggested, you need to update webpack's config with the target pointing at the correct electron.
In case you're using one of the electron builder boilerplates, you might see directory .erb with a few webpack configs. Especially, there might be one with target = "electron-main" and another with target = ["web", "electron-renderer"]. So it feels like mission accomplished; however, according to webpack's doc on target, if you pass a list, then a common config is set. Since web doesn't include fs, the common part also won't include fs.
For the reason above, some of electron imports, e.g. clipboard, can be only used from the "electron-main" side of your application.
The work-around, e.g. using clipboard on the application side, is to use IPC to communicate between main and renderer sides.
Check your webpack.config.js. Looks like you target is not electron-main or electron-renderer.
I will clarify my question for a very specific use case.
Let's say, I've designed my code as follows:
And this is the content:
src/inner/inner.darwin.ts:
export default function logger() {
console.log('Darwin Logger');
}
src/inner/inner.windows.ts:
export default function logger() {
console.log('Windows Logger');
}
src/inner/inner.ts:
NOTE the export default - it is mandatory
import LOGGER from '???????????';
export default class A {
public static logger() {
LOGGER();
}
}
So basically, when I compile Windows code, I want to import the inner.windows.ts code. When using Mac code - inner.darwin.ts code.
I do know I can control excluded files in tsconfig.json.
So when I'm going to create Windows application I will code:
"exclude": ["**/*.darwin.ts"]
And when I will create Mac one:
"exclude": ["**/*.windows.ts"]
However, it did not help for resolving the import issue.
I do know I can run code depends on the underlying platform using process.platform, but it does not do the job. I want the output Windows application to be more small of size - and ignore any Mac code. If I'd use this process.platform - the Mac code would still exist in the underlying Windows application.
Any advice?
Can Webpack do something to help?
I am open for any code changes as long as the code is well defined and split, and the final outcome is that I have only Darwin code and Windows code
So the solution is quiet simple. All I had to do is using a Webpack plugin.
The best Plugin to fit this task is NormalModuleReplacementPlugin which is provided out of the box by Webpack itself.
The exclude in tsconfig.ts is no longer reasonable within this solution.
Simply provide the following plugin:
new webpack.NormalModuleReplacementPlugin(
/darwin/,
function (resource) {
resource.request = resource.request.replace(
/darwin/,
'windows',
);
}
),
This will override any import to a darwin file into a winodws file.
Now, in development, both files exist - but in compilation - only either of them will exist.
inner.ts simply becomes:
import LOGGER from './inner.darwin';
export default class A {
public static logger() {
LOGGER();
}
}
Of course that could be quiet cumbersome to go into webpack.config.ts each build, so we could definitely run some extra webpack script to decide upon which underlying build to go with, for example:
const appTarget = env.APP_TARGET || 'darwin';
And then simply use this variable within the plugin, and run your npm build script providing an APP_TARGET argument.
Following is the entry point to my library, it generates a component with a dynamic tag:
// muvement.js
import { defineComponent, ref, onMounted, h } from 'vue';
const createMuvement = (tag) => {
return defineComponent({
name: `m-${tag}`,
setup(props, context) {
const root = ref(null);
onMounted(() => {
console.log(root.value);
});
return () => h(tag, { ...context.attrs, ref: root }, context.slots);
}
});
};
const muvement = (...tags) => {
const components = {};
tags.map((tag) => (components[`m-${tag}`] = createMuvement(tag)));
return components;
};
export { muvement };
It's expected to be consumed like so:
// Home.vue
<template>
<div>
<m-div>div</m-div>
<m-button>button</m-button>
</div>
</template>
<script>
import { muvement } from "muvement";
export default {
name: "Home",
components: {
...muvement("div", "button")
}
};
</script>
This works as expected when the library code is contained within the Vue app folder (assuming we are now importing from "#/components/muvement.js" instead of "movement").
That is:
-muvement-test-project (scaffolded with vue-cli)
- src
- views
- Home.vue
- components
- muvement.js
I've also published an alpha release that works fine when importing "muvement" after installing it directly from the npm registry (that is, npm install muvement instead of npm link muvement).
The Problem
During development, I want an app to test the library with that is separate from the library's directory.
I've used npm link to link the library to the test app (as I have done with many other projects in the past).
From /path/to/library
$ npm link
From /path/to/test/app
$ npm link muvement
So far so good. The module is available as a symlink in the test app's node_modules folder. So I import { muvement } from "muvement", run npm run serve, and... BOOM.
Everything explodes (see errors below). It's also probably worth noting that trying to import from the full path (i.e. C:/dev/npm/muvment/dist/es/index.js) results in the same issues as npm link does, so I don't think it has anything to do with the symlink directly.
This is what appears in the console:
For pretty much the entire day I have been trying to solve this one issue. I've seen several seemingly similar questions that were solved by settings Webpack's resolve.symlinks to false but that has no effect on my problem. I've read all through the docs and even Vue's source code (here is the offending line for those who are curious).
Since the warning suggests that the error is commonly attributed to async setup I thought maybe webpack was doing something weird that would make my code async. This doesn't seem to be the case as the call stack of both the working attempt and failed attempt are identical.
What's not identical is the scope.
Here is the scope for the example that is working:
And here is the failing one:
(Notice that the target parameter is null during the call to injectHook, which is obviously what prompts Vue to show a warning).
My question is, why does the location of the imported module make such a difference during the execution of the said module?
The library code and build setup are available here:
https://github.com/justintaddei/muvement
The test app is available here:
https://github.com/justintaddei/muvement/tree/example
If I've left out something important, please let me know in the comments. It's been a long day so I'm sure I've probably missed something.
Thank you.
The problem is your app is using two different vue dependencies under the hood - vue requires the same dependency to be used to keep track on reactivity, lifecycle, etc.
When you link a library npm/yarn will use that linked folder node_modules, but your app is using it's dependencies from it's node_modules.
When your app imports vue it will go app/node_modules/vue but when you import from your linked dependency it will be going to linked_dep/node_modules/vue.
app
node_modules
vue
linked library
node_modules
vue
One easy way to debug this issue is to change both vue dependency files with a console.log and check if the console is logging both.
(clone the repo & try it)
I'm working on a desktop app using node, create-react-app, and electron. The following line, recommended here
const { dialog } = require('electron').remote
when added to my store react component (and thus not in the electron main process), causes the following error:
TypeError: fs.existsSync is not a function
getElectronPath
F:/freelance/repos/creative-ontology-editor/node_modules/electron/index.js:8
5 | var pathFile = path.join(__dirname, 'path.txt');
6 |
7 | function getElectronPath() {
> 8 | if (fs.existsSync(pathFile)) {
9 | var executablePath = fs.readFileSync(pathFile, 'utf-8');
10 |
11 | if (process.env.ELECTRON_OVERRIDE_DIST_PATH) {
I believe this is because React or create-react-app specifically blocks/nullifies certain node.js modules like fs, but I could be wrong. Note that this error happens inside the electron module when I include the above line, not in my own code.
My goal is to have my desktop app able to save and load files to the user's machine, the way something like Word or Excel does.
I've called const fs = window.require('fs'); in my react component, which I think I'm not supposed to do, but also, since this is in the actual electron module's index.js, I made sure that it also calls it, which it does: var fs = require('fs'). There was no change in behavior when I switched my call in the react component to const fs = window.require('fs').
I've also made sure to set webPreferences.nodeIntegration to true in my electron main process, to no avail.
So I've been learning react, and wanted to make a basic firepad instance. My current setup is having one container div in my index.html, and having all of my react components rendering through that div. My current attempts and the code I'm showing with this have been in an environment with gulp and browserify, but I'm also playing around with ES6 and webpack. So I'm pretty flexible about getting this working as I learn. Here's the code:
"use strict"
var React = require('react')
, Firebase = require('firebase')
, fbRoot = 'myURL'
, CodeMirror = require('codemirror')
, Firepad = require('firepad')
, firepadRef = new Firebase(fbRoot + 'session/')
, myCodeMirror = CodeMirror(document.getElementById('firepad'), {lineWrapping: true})
, myFirePad = Firepad.fromCodeMirror(firepadRef, myCodeMirror, { richTextShortcuts: true, richTextToolbar: true, defaultText: 'Hello, World!'});
var WritePage = React.createClass({
render: function(){
return (
<div>
<div id="firepad"></div>
</div>
);
}
});
module.exports = WritePage;
The first error I was getting was that it couldn't find the codemirror.js file. Although CodeMirror was being correctly defined in Chrome's dev tools, I moved that from requiring the npm package to just linking the 2 needed codemirror files to my html. It then gave me an error about not being able to take .replaceChild of undefined. I then tried moving all of the dependency files over to my index.html, but still had the same .replaceChild error. Anyone have any experience with react and firepad? I read in the reactfire docs that it's one way binding from firebase to my site, which for my case making a read-only firepad would be fine. Like I said, I'm flexible all of this stuff is new to me.
From the link that Michael provided.
The problem is that you are trying to reference a DOM element before React has rendered your component.
, myCodeMirror = CodeMirror(document.getElementById('firepad'),{lineWrapping: true})
, myFirePad = Firepad.fromCodeMirror(firepadRef, myCodeMirror, {richTextShortcuts: true, richTextToolbar: true, defaultText: 'Hello, World!'});
By moving this code into componentDidMount(), it runs after the CodeMirror DOM element has been constructed and you'll be able to reference the DOM node. You will also probably find it easier to use the React ref attribute instead of document.getElementById().
Use these npm packages - brace, react-ace, firebase, firepad.
Since firepad needs aceto be present globally, assign brace to global var
like(not the best way, but works) before importing firepad
import firebase from 'firebase/app';
import 'firebase/database';
import brace from 'brace';
global.ace = brace;
global.ace.require = global.ace.acequire;
import Firepad from 'firepad';
Use ref to get instance of ReactAce and initialize it in componentDidMount using:
new Firepad.fromACE(this.firepadRef, this.aceInstance.editor, options);
Similarly for CodeMirror editor.
Hoping, this would be of some help.