Skip to content

react-rails incompatible with browserify workflow #129

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

Closed
AndrewRayCode opened this issue Dec 8, 2014 · 8 comments
Closed

react-rails incompatible with browserify workflow #129

AndrewRayCode opened this issue Dec 8, 2014 · 8 comments

Comments

@AndrewRayCode
Copy link

I'm getting an eval error on rendering hitting a page.

Unexpected token < at <eval>:18117:14

I see this in my server logs:

ActionView::Template::Error (Unexpected token < at <eval>:18117:14):
    17:
    18: <body>
    19:
    20: <%= react_component( 'Header', {}, { :prerender => true } ) %>
    21:
    22: <h2>Edit <%= resource_name.to_s.humanize %></h2>
    23:

Template code:

<%= react_component( 'Header', {}, { :prerender => true } ) %>

components.js:

//= require_tree ./templates

templates/Header.js:

/** @jsx React.DOM */
var React = require('react'),

module.exports = React.createClass({
    displayName: 'AppView',

    render: function() {
        return 'Hello World';
    }
});

If I remove the prerendered => true I get the empty div outputted to the page, no errors. I'm intentionally not using react in the rails asset pipeline, I have react installed with npm. But the react_component tag works without prerender.

Gemfile:

gem 'react-rails', '~> 1.0.0.pre', github: 'reactjs/react-rails'
gem "therubyracer", :platforms => :ruby
gem "therubyrhino", :platforms => :jruby
gem "sprockets"

My header component builds fine with browserify so the dependency tree isn't the issue. How can I figure out what's going on here?

@rmosolgo
Copy link
Member

rmosolgo commented Dec 8, 2014

Hmm, I can't tell that this is related, but here's what jumped out at me:

When you pass a component name to react_component, that string is looked up in global scope (after loading components.js, source). Are you assigning your component class to global variable Header anywhere?

(I'm not sure why this would cause "unexpected token <" but it's a start .... )

@AndrewRayCode
Copy link
Author

I'm making some progress, but this gem is putting up a fight.

I did get a bare bones component to render, buuuuut...

The < error was because if you have any files that are JSX in your javascripts/, even if they have /** @jsx React.DOM */ at the top, will cause the runtime to error. After re-reading the readme I renamed them to .js.jsx, but the file comment should probably make that automatic?

I isolated my header file into its own folder for now and specified that in components. Here's the errors I encountered:

require is not defined. This is a huge problem. The node and browserify ecosystem mainly use requireJS now to set up modules. Does Rhino not have a module loader in core? I'm way behind on my Rhino knowledge.

module is not defined "that string is looked up in global scope" no no no! The majority of react developers use browserify with requirejs to organize their code. We can't put every module in the global scope.

It seems like any changes to the javascripts files needs a server restart to take place, which slows down progress.

I tried pointing the components directly to my browserify-built bundle.js file, but I realized I have things specified with browserify-shim so they don't get built into the bundle. For example, in my package.json I have:

"browserify-shim": {
    "underscore": "global:underscore"
},

And I include underscore on the page in a regular script tag so it's cached from a cdn. That means underscore isn't built into bundle.js. I'm going to try putting my deps into the the components folder but I won't have any gaurantee they are run in order, right? How could I make sure to define a missing file?

My test suite executes some of the files using node, which of course works fine because require works. I don't have to point node at my bundle.js file. I'm not sure what the ideal solution is.

Right now this gem seems incompatible with a real React workflow. I'm going to keep fiddling with it and see if I can find any tricky ways to get require to work.

@AndrewRayCode AndrewRayCode changed the title Eval error with prerender react-rails incompatible with browserify workflow Dec 8, 2014
@xionon
Copy link
Contributor

xionon commented Dec 9, 2014

Hi! I'd like to help, but I think mostly I've just got bad news.

The < error was because if you have any files that are JSX in your javascripts/, even if they have /** @jsx React.DOM */ at the top, will cause the runtime to error. After re-reading the readme I renamed them to .js.jsx, but the file comment should probably make that automatic?

This is how Rails expects things to work. You read the file extensions backwards and apply pre-processors in order. You can do crazy things, like foo.js.coffee.erb, which will first run ERB, then Coffeescript, then serve the file as Javascript. react-rails is following this convention.

require is not defined

This is because we're stuck with the Rails Asset Pipeline. JavaScript is parsed on the server-side using ExecJS, another Rails core component. From the ExecJS FAQ:

Why can't I use CommonJS require() inside ExecJS?

ExecJS provides a lowest common denominator interface to any JavaScript runtime. Use ExecJS when it doesn't matter which JavaScript interpreter your code runs in. If you want to access the Node API, you should check another library like commonjs.rb designed to provide a consistent interface.

There's really not much we can do about that without requiring some non-standard Rails libraries, which IMO is beyond the scope of this gem. If you really need require, I suggest setting up a separate build step.

module is not defined "that string is looked up in global scope"

Again, this is a limitation of ExecJS: for server rendering in a Rails environment to work, components.js needs to include the component in a way that makes it globally accessible. Theoretically, only the "top" component needs to be reachable globally, the rest can be hidden away.

It seems like any changes to the javascripts files needs a server restart to take place, which slows down progress.

Are you working off the master branch? If so, this is a bug, you should not need to restart the server every time a file needs to be changed. If you can create a reproducable case, I can take a look at it.

I'm going to try putting my deps into the the components folder but I won't have any gaurantee they are run in order, right? How could I make sure to define a missing file?

Rails Asset Pipeline has it's own workflow for requiring dependencies in order. If you add //= require underscore to the top of your component file, the Asset Pipeline will make sure that 1 instance (and only one instance) of underscore.js is included before your component, assuming underscore.js is in the Asset Pipeline's lookup path.

We're stuck with the Asset Pipeline, because that's what Basecamp likes. Theoretically, you could have a "side build" based around Node + g(ulp|runt), with components.js as your output target. So long as you made your top-level component globally available, ExecJS should be able to figure it out.

@rmosolgo
Copy link
Member

rmosolgo commented Dec 9, 2014

👏 👏

@xionon
Copy link
Contributor

xionon commented Dec 9, 2014

Speaking of "side builds," it looks like @bogdan-dumitru in issue #120 has a pretty good build going.

@olance
Copy link
Contributor

olance commented Jan 11, 2015

Maybe this article could help you out: https://medium.com/@olance/rails-react-browserify-e315001d5974 ?

@rtorr
Copy link

rtorr commented Feb 5, 2015

Would it be possible to have a separate browserify bundle // required in as a top level component, then all of the require/module problems go away?

@rmosolgo
Copy link
Member

Sounds like there are a few OK workarounds!

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

No branches or pull requests

5 participants