Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create-React-App #30

Closed
AnthoniG opened this issue Mar 4, 2018 · 22 comments · Fixed by #95
Closed

Create-React-App #30

AnthoniG opened this issue Mar 4, 2018 · 22 comments · Fixed by #95

Comments

@AnthoniG
Copy link

AnthoniG commented Mar 4, 2018

Does anyone know how to get this working with Create-React-App ?
I've been struggling all day and I am still nowhere forward :(

It works if I build the CRA itself BUT I have to keep doing this every time I change something. Create-REact-App comes with it's own Dev server that hot reloads and I wanted to be able to plug into it but when I do a console.log(eel) it always prints undefined.

@ahopkins
Copy link
Contributor

ahopkins commented Mar 5, 2018

How about something like this to reload that app: https://github.com/AoiKuiyuyou/AoikLiveReload

I have not tried it... but I plan to.

@ChrisKnott
Copy link
Collaborator

@AnthoniG this is something I want to get working, but I don't have time until next month. If you get it working in the meantime I please post here

@ahopkins
Copy link
Contributor

ahopkins commented Mar 7, 2018

I played around with this for just a few minutes. I did it using VueJS instead, but the concept should be the same. It is not yet an elegant solution, but it works without making any modifications to Eel.

Files

Here is my file structure.

.project
├── app.py
├── start.sh
└── web
    ├── build
    ├── config
    ├── index.html
    ├── node_modules
    ├── package.json
    ├── README.md
    ├── src
    ├── static
    └── yarn.lock

Here is start.sh:

#!/bin/bash

cd web
yarn run dev

And, here is app.py

import eel
import threading
from subprocess import call
from time import sleep


def start_web():
    call(['./start.sh'])


def start_eel():
    sleep(4)
    eel.init('web')
    eel.start('', options={
        'port': 8080,
        'host': 'localhost',
    })


if __name__ == '__main__':
    t1 = threading.Thread(target=start_web)
    t2 = threading.Thread(target=start_eel)
    t1.start()
    t2.start()

Plan

As you can see, the idea is simple: start both the eel webserver, and whatever other webserver you want. Then, tell the eel browser to open your other webbrowser instead of a static file.

Pitfalls

I hacked this together in just a few minutes as a proof of concept. Clearly we can improve upon this, and looks like something that we could and should be able to bake into eel itself.

As you can see, I needed to throw a sleep in there just to make sure the webpack server was up and running first. This needs to be fixed.

Maybe also there is a better solution to using Threading

This may not be the best solution, but it does work. I was able to startup the servers, and make changes to my Vue app and have it live reload inside of the eel browser.

Kudos to @ChrisKnott for an awesomely simple and elegant implementation.

@ahopkins
Copy link
Contributor

ahopkins commented Mar 7, 2018

I have been playing around with this, and I think I have an idea on how to set this up with an API sort of like this:

eel.init('web')
eel.start(
    {
        'port': 8888,
        'host': 'localhost',
        ‘path’: ‘’
    },
    services=[
        {
            ‘dir’: ‘some/working/path’,
            ‘exec’: ‘my_executable_command.sh’
        }
    ]
)

The thought would be to block the run_lambda function if services is present. In that case, Eel would go out and startup whatever services it could, and once they were up and running, continue on with run_lambda.

The second change would be to make the first parameter in start to be either a string or an object. If it is an object, then it would be used for setting the location of the browser so that the options keyword argument could still be used for gevent.

@ChrisKnott If you agree with this approach, I’d be happy to put together a PR for you. Am I on the right track?

@ahopkins
Copy link
Contributor

ahopkins commented Mar 8, 2018

I created a PR #33. This allows users to set any arbitrary location inside eel.start.

The effect of this would be to serve (for example) an application running from npm run dev.

HOWEVER, this does not cover the second half of launching services. I am hoping to get some feedback on @ChrisKnott about my strategy if it is a worthwhile endeavor or not.

This could (as shown above) be something entirely handled by app.py and not be bundled inside of eel. I just happen to think it would be a nicer API if eel could manage spinning up the other services as well.

Of course, in the react or vue instance, this would be in development only. When packaged up, this would be bundled into static files that COULD be served by eel.

@ahopkins
Copy link
Contributor

ahopkins commented Mar 8, 2018

Here is an example of how this is running using Threading outside of eel.

https://github.com/sockpuppetapp/sockpuppet

@AnthoniG
Copy link
Author

AnthoniG commented Mar 8, 2018

@ahopkins Loving the idea and going to play around with your git
Also going to have a play around with React and see what that thinks of this. Like I mentioned I got it to partially work, but this looks to be a cleaner solution.

BTW: How did you get it to not complain about address in use ?

Also, what do you do for a build? Have seperate app.py or conditional checks to see if in production etc ?

@ahopkins
Copy link
Contributor

ahopkins commented Mar 8, 2018

@AnthoniG

BTW: How did you get it to not complain about address in use ?

The options parameter is used to tell gevent which port to hook up to. So, I tried to separate the location of the browser from the binding of gevent. The way I have it running actually is to let my webpack service run on 8080, and I changed gevent to 8888. As long as they are running on different ports, all is fine.

page = {
    'port': 8080,
}
eel.start(page, options={
    'port': 8888,
})

Right now, I am just launching webpack in a separate terminal session waiting to head back from @ChrisKnott. I think the best route would be to create a services parameter that can spawn new processes in an identical manner as used already, and then initiate the process, also as currently done for launching the browser.

Also, what do you do for a build?

I have not tested it out yet. But, conceptually, there would be at least a different operation to run. Once there is a build of the Javascript app, that could be handled statically by gevent and served without needing to get another process in there. I have not tried it yet, but it would be no different conceptually from what is on the README.

Have seperate app.py or conditional checks to see if in production etc ?

Perhaps. Or, rather than building the conditionals into it, just replace the script with a build version. I suppose it depends how much abstraction gets introduced.

@AnthoniG
Copy link
Author

AnthoniG commented Mar 8, 2018

@ahopkins Thanks for that. I won't be able to fully test it until this weekend. My idea then is to use your code as a base and re-factor it for my Create-React-App version of it.

Before this, I was thinking of going the way of using React in the browser and compile in-memory like JSBin / JSFiddle et al. do

@ahopkins
Copy link
Contributor

ahopkins commented Mar 9, 2018

Awesome. Can you post your code here? Curious to see how your solution may differ

@AnthoniG
Copy link
Author

AnthoniG commented Mar 9, 2018

@ahopkins Yep can do, although it won't change much. Will just use Create-React-App itself rather than VueJS.

The other method of having it compile in-memory I never got around to testing, but was going to be doing it all over the weekend.

@AnthoniG
Copy link
Author

Here's my code. Like I said it's based on your VueJS code 😺
https://github.com/AnthoniG/eel_react

Need to play around with it and work how to do a build / deploy scenario but other than that it works. Guess just need main Eel distro to be altered with your pull request.

Also included a requirements.txt BUT it's listing Eel as the one from Pypi. However I did a pip install git+ from @ahopkins REPO 😄

@AnthoniG
Copy link
Author

I've hit a problem on the Python call to Javascript side of things.
No matter what I do, I can not get Python to call a Javascript function.

It keeps throwing an AttributeError and crashes (I wrapped it in a try..except to prevent that, but it still happens)

I will amend my repo with the code I have and hopefully some one can figure it out.

@ahopkins Your code works fine (I only put the JavaScript function in index.html, did not try it further down the pipeline because I don't understand Vue that much)

@ahopkins
Copy link
Contributor

I was planning to try and get eel to be exposed inside webpack tonight. I’ll post any code changes I have. Push what you have to your repo and I will be happy to take a look.

@AnthoniG
Copy link
Author

@ahopkins Code updated.
You will see in App.tsx (my main app entry) that I have several comments in there that explain how I was trying to setup the functions.

Files changed :-
eel_react.py
App.tsx

Files Added :-
src/classes/eel.tsx
(this is because Typescript requires a definition file and this will allow me to call eel functions from anywhere in my code by simply importing this file.)

[Will work on a proper .d.ts file later, once I've got everything working lol]

@ahopkins
Copy link
Contributor

ahopkins commented Mar 10, 2018

@AnthoniG I figured out the problem. Actually, there were a couple.

  1. The way that eel is "exposing" methods in JS is by crawling the source files looking for eel.expose. In order to get it to look for methods in a tsx file, I needed to add it to __init__.py in the eel package:
    allowed_extensions = '.js .html .txt .htm .xhtml .tsx'.split()
  1. There was a sort of scoping problem because the function being exposed was inside a component. Once I moved it outside into eel.tsx, it worked fine.

I think this sort of underscores a potential flaw in the setup of Eel. No offense to @ChrisKnott, but this method I think is not scalable for an app built on (for example) React, and may potentially be flawed. The startup time is delayed quite a bit because of the size of node_modules directory. Granted, once built, this would not be a problem.

I would rather see eel.expose in the JS be a call to the Websocket that pushes the name of the method. This would make it a little more transparent what eel.expose is doing, and would allow for dynamic changes to the exposed functions, rather than everything being declared up front at initialization.

@AnthoniG
Copy link
Author

AnthoniG commented Mar 10, 2018

@ahopkins Do I need to re-pull Eel from your git then in order to make this work ?

Also agree on the once built part all of this work around and hiccups go away. However during building it's a pain.

I have been looking around for alternatives and exploring them.
(IE: Using jquery and some other plugins etc, look for a book by Michael S Mikowski)

Github link to Single Page Web Application

@ahopkins
Copy link
Contributor

You would need to stash a local copy of the eel repo, and make the change. I have not committed it.

git clone https://github.com/ahopkins/Eel.git

Then, install that.

pip install -e /path/to/Eel

Go into Eel/eel/__init__.py and add .tsx into allowed_extensions as shown above. That will get it working for now until there is a solution. Your alternative would be to define all of your eel methods in a file with a .js extension.

@ChrisKnott
Copy link
Collaborator

ChrisKnott commented Apr 3, 2018

@ahopkins I am not familiar with React at all (my background is in games dev and C++ mainly!) do you mind if I add you as a collaborator to the project, then I can stand back and let you integrate the React things?

I honestly wouldn't even know how to go about testing it!

@ahopkins
Copy link
Contributor

ahopkins commented Apr 3, 2018

Awesome. I would love very to help out. Not sure I'd call myself a React guru, although my main background is as a full stack web developer. I am happy to help and provide my input.

@yihengli
Copy link

@ahopkins Not sure if anyone is still working on this issue. If so, maybe it would be nice to open it as a separate branch so everyone can check the latest progress and potentially contribute.

I do agree that eel should have a way to build with popular framework such as React, Angular or Vue. Especially for the complied files of those front-end frameworks, I wondering if there is an elegant way for eel to detect those "exposures". :/

@ahopkins
Copy link
Contributor

Yes, I am working on it. I have not committed code because it still needs some more tests and (quite honestly) the last couple weeks were simply too busy for me. Hopefully I will have a chance to push soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants