How to serve minified React with Cherrypy?

60 views
Skip to first unread message

Edvin Beqari

unread,
May 18, 2019, 5:40:34 AM5/18/19
to cherrypy-users
Hello, 

I posted this question on Stack Overflow but then I found this group. 


As I explain in my question, I tried to embed the index.html fromt he react-app in the server index.html as explained in the docs. 

This is working somewhat but is there a proper way to accomplish this?

Thank you

Cameron Simpson

unread,
May 21, 2019, 7:25:02 AM5/21/19
to cherryp...@googlegroups.com
On 17May2019 17:48, Edvin Beqari <beqa...@gmail.com> wrote:
>I posted this question on Stack Overflow but then I found this group.
>Here is a link to my question with all the details:
>https://stackoverflow.com/questions/56195059/how-to-serve-minified-react-with-cherrypy

It is generally polite to post the whole question here. So that people
don't need to go elsewhere and also so that the list archives have
plenty of context when people do searches.

>As I explain in my question, I tried to embed the* index.html* fromt he
>react-app in the server *index.html* as explained in the docs.

What you're doing seems a little complex (as does _every_ bloody web
tutorial I've seen about this stuff). Let me describe what I'm doing,
since I'm making a ReactJS web app with a Python backend. My backend is
Flask instead of CherryPy, but the approach should map across very
directly.

I've got a Flask app with the "/" endpoint returning an index.html
template which looks like this:

Web Application Title
<hr>
<link href="{{ url_for('static', filename='app.css') }}" rel="stylesheet" type="text/css" />
<div id="app-top-div"/>
<script src="{{ url_for('static', filename='app.js') }}"></script>

After you expand the template that just links in an app.css for style
and an app.js for the JavaScript application. You could as easily use a
static bit of HTML without the template.

We'll ignore the app.css, it is just static CSS content.

Like app.css, app.js is physically stored in static/app.js in the Python
app - put it wherever static files go in your CherryPy app. It is
compiled from the source app.js and the node_modules using browserify.

The source app.js just starts like this:

var React = require("react");
var X = console.log;
var J = JSON.stringify;
var ReactDOM = require("react-dom");
var Form = require("react-jsonschema-form").default;
var rp = require("request-promise");

and underneath that the application javascript. X() and J() are just
shorthands to aid debugging.

So, to the static/app.js file. I build it like this:

browserify -t [ babelify --presets [ react ] ] app.js >path/to/static/app.js

So (a) I'm using browserify instead of webpack and (b) it isn't minified
(this aids debugging when I'm lucky).

Browserify is just a symlink to node_modules/browserify/bin/cmd.js, and
my local node_modules is maintained with yarn.

This might let you simplify your setup and help figure out what webpack
is unhappy about. Or browserify might have a minify mode - I haven't yet
checked.

Cheers,
Cameron Simpson <c...@cskk.id.au>

Edvin Beqari

unread,
May 26, 2019, 9:13:45 AM5/26/19
to cherrypy-users
Thank you Cameron. I went down the rabbit hole of webpack and browsify but I just could not get the config right. 

Instead, I I built the client with the default create-react-app configuration (chunks the javascript and creates a template) and I simply pointed the server to the build directory. 

class Server(object):

@cherrypy.expose
def index(self):
return open('../cococlient/build/index.html')

def cors():
cherrypy.response.headers["Access-Control-Allow-Origin"] = "*"

if __name__ == '__main__':
conf = {
'/': {
'tools.staticdir.root': os.path.abspath(os.getcwd())
},
'/catalog': {
'tools.CORS.on': True,
'tools.response_headers.on': True
},
'/static': {
'tools.staticdir.on': True,
'tools.staticdir.dir': '../cococlient/build/static'
}
}

server = Server()
server.catalog = Catalog()
cherrypy.tools.CORS = cherrypy.Tool('before_handler', cors)

cherrypy.quickstart(server, '/', conf)

Cameron Simpson

unread,
May 28, 2019, 11:53:11 AM5/28/19
to cherryp...@googlegroups.com, beqa...@gmail.com
On 26May2019 06:13, Edvin Beqari <beqa...@gmail.com> wrote:
>Thank you Cameron. I went down the rabbit hole of webpack and browsify but
>I just could not get the config right.

I'm surprised. It took me some hours (switched node package managers at
least once to get to yarn, again courtesy of the various JS setup
tutorials rabbit hole, ugh). But...

>Instead, I I built the client with the default create-react-app
>configuration (chunks the javascript and creates a template) and I simply
>pointed the server to the build directory.

I started with create-react-app, since I was new to ReactJS. Gahhhh!!!

It makes an obscenely complex tree with your programme source embedded
deep within it. A nightmare for both navigation and version control
inclusion/exclusion.

After wasting an hour on it I tossed it and backed off to the bare
minimum: a node_modules tree, a python source tree and a webapp tree
with (presently, and possibly permanently) just a single file in it.

And then a Makefile to run browserify on that JS file to make the static
one in the python app static files directory.

Looking at your code below...

>class Server(object):
>
>@cherrypy.expose
>def index(self):
>return open('../cococlient/build/index.html')
>
>def cors():
>cherrypy.response.headers["Access-Control-Allow-Origin"] = "*"
>
>if __name__ == '__main__':
>conf = {
>'/': {
>'tools.staticdir.root': os.path.abspath(os.getcwd())
>},
>'/catalog': {
>'tools.CORS.on': True,
>'tools.response_headers.on': True
>},
>'/static': {
>'tools.staticdir.on': True,
>'tools.staticdir.dir': '../cococlient/build/static'
>}
>}
>
>server = Server()
>server.catalog = Catalog()
>cherrypy.tools.CORS = cherrypy.Tool('before_handler', cors)
>
>cherrypy.quickstart(server, '/', conf)

I suspect all you'd need to do is get browserify or webpack to write out
your app JS into the static directory you configure above.

(And because it is just one JS file from the same site as the Python app
there's no CORS stuff.)

I am running browserify in the same directory that has my node_modules
subdirectory; I think that was all that was needed for it to find
everything.

Of course one has to fetch whatever modules are required, but that is
just "yarn install", eg (from my shell history) "yarn install --dev
@babel/core". Since yarn maintains a "yarn.lock" file with the module
install state in it, provided you revision control that (commit after
adding/updating stuff) then the yarn install in a clean checkout is just
"yarn install" as it gets everything from the yarn.lock).

Cheers,
Cameron Simpson <c...@cskk.id.au>
Reply all
Reply to author
Forward
0 new messages