Blaze <==> React Refactor Paths?

Are there any plans for refactor paths between Blaze and React components? How do we convert a Blaze app into React? Or a React app into Blaze? Are there any plans for an intermediary step, where the JSX and JSS are stored in separate files? And are there any plans for transpilers and/or refactor tools that will convert HTML into JSX and vise versa? And CSS into JSS (or whatever React’s equivalent is) and vice versa?

I’m thinking along the lines of the following:
[HTML, CSS JS] <==> [JSX, JSS, JS] <==> JS[JSX, JSS]

Well to start moving from Blaze to React you can use the react-template-helper package that allows you to include React components into Blaze templates, so that you can slowly refactor your app.

See more here: http://react-in-meteor.readthedocs.org/en/latest/react-template-helper/

1 Like

I’m not looking to slowly refactor an app. I’m looking at how to manage dozens of apps; with hundreds of component; with multiple team of programmers. Some who are familiar with React; some are not.

Maybe the designer for the project changes; which puts us in a position to refactor all the CSS into JSS as we onramp the new person. How would we go about that? Or maybe we’re ready to convert all the JS, but aren’t ready to make the leap to JSX quite yet. How do we upgrade to React while still using Blaze html templates? What happens if we find that we need to go the other direction? For example: an app gets written in React; but then it’s decided that one package consisting of marketing materials should be assigned to a senior developer with expertise in business development but not javascript. How should we convert from React to a more traditional HTML oriented Blaze solution that the senior dev can work with?

Maybe the react-template-helper package and existing transpilers can handle those use cases. If so, they really need to be documented somewhere. They don’t seem to be in the existing docs.

I suspect that React might not be a good choice for your organization if people who are working on those projects aren’t going to learn how to use React.

2 Likes

I work with multiple client organizations. :wink:

But, yes, I agree with the point. Which is why refactor tools need to go both ways, for Meteor to succeed in having these multiple view layers. React is a very performant solution; at the cost of not being ubiquitous. Some tasks require ubiquity, and being able to outsource to India, or hire on interns and junior level devs, or assign to designers and other non-devs.

With the right refactor tools and abstractions; this won’t be an issue. Need an app more performant? Follow these steps to convert the codebase to React and use a pure Javascript solution. Need it to be more ubiquitous? Follow these other steps to convert the codebase to Blaze which supports traditional HTML/JS/CSS that a billion people know.

I think maybe you are asking for a compiler? That is not an easy task, I bet it would cost a lot of manpower to make something barely usable.

Would be really cool though.

3 Likes

I don’t see how any automation of the process could be possible.

Take the events for instance. Blaze works with event delegation. You can have a template PostsContainer with multiple instances of the template Post inside. Let’s say that there is a h2.postTitle in the Post template with no event attached on the Post level. It would not be absurd to have something like that :

 Template.PostsContainer.events ( {
    'click .postTitle' () {
        // Do something
    }
} );

In React the event would have to be declared on the .postTitle element.

I do not see any possible compiler smart enough to do kind of transpilation. Because the post would have to be called like that in the PostsContainer component :

....
onPostTitleClick () {
    // Do something
},
... render( ... return ...
<Post onPostTitleClick={ this.onPostTitleClick } /> 

And the event declared that way in the Post component :

<h2 onClick={ this.props.onPostTitleClick  }>{ this.props.title }</h2>

And maybe the Post component can be called in another context (the reason why the click was on the container) so you could have to add condition like onClick={ this.props.inContainer && this.props.onPostTitleClick } (well, no need here because it would just return false if it’s undefined)

And I’m not talking about CSS. They would be a complete nightmare. At the moment, I use Radium in my React apps. Just imagine all the states, pseudo elements, hover… They would simply stop existing.

When developing in blaze, I use a lot of pseudo elements. Which cannot exist once inlined.

In brief, just do it by hand with developers knowing what they do. An automated process would be a cornucopia of bugs.

Actually, yeah… I think you’re right, @muaddib. Your post got me thinking, and maybe what I’m trying to say is this: for full React integration with Blaze, the Meteor builder should support .jsx and .jss file extensions.

Then, we can do things like run jss source.css -p > source.jss across all of our CSS files, using jss-cli, and all of our CSS files will be converted into JSON objects; preparing them for being moved into the React .js file.

The HTML to JSX would be a similar refactor. Although it would be really nice if a .jsx file didn’t have to have the require statement and the assignment to a variable. That’s something that would be very appropriate for the Meteor bundler to do; just as it currently wraps and links files during the bundling process.

If the Meteor bundler understood what to do with .jss and .jsx file extensions, and knew how to attach those objects to similar named .js files as part of a React component, it would be a huge win. All the existing Blaze users would have a clear refactor path to using React (or vice-versa, for people needing to go the other way).

The react package supports .jsx (and .jss ? I don’t know)

Let’s say you have :

Template.Foo.helpers( {
	target() {
		return 'foo';
	}
} );

<template name='Foo'>
	<p>my text</p>
	{{#Button
		target = target
		location = 'bar'
	}}
		baz
	{{/Button}}
</template>

<template name='Button'>
	
	<button>
		{{#if target}}
			<label>{{target}}</label>
		{{/if}}
		{{location}} {{> UI.contentBlock}}
	</button>

</template>

How does the transpiler knows what is a property, a state, a variable it should put in the render function, a function it should build in the component root… Why would be target in the Button component be a props, but a function in the Foo component. You would have to analyse also all the javascript.

Jsx is html + js. You cannot do html to jsx. You have to do all your html + js to jsx. The meta analyse would be so complex.

I mean, it would be an amazing piece of code. But so much more expensive than just doing the changes by hand.

Sounds like a job for a build plugin. Defining handlers for special file extensions is something Meteor actually gives you a lot of tools for (and is how we built the default jsx package itself!)

2 Likes

Just use Webpack to build your React code. You get the added benefit of being able to do ES6 imports instead of dealing with global/local vars and file load order in pure Meteor. It outputs bundles that meteor can consume, and only took me a few hours to get working. No need to clutter the Meteor codebase with anything to support React. The only small issue I haven’t solved yet is how to get meteor to pull in the source maps that Webpack generates.

1 Like

Are you doing this with a build plugin, or some other method? If you put the Webpack compilation logic into a build plugin, you should be able to get source maps working quite easily. Here’s an example for Browserify: https://github.com/elidoran/cosmos-browserify/blob/master/plugin/browserify.coffee

This approach will become much faster/better in Meteor 1.2, which will introduce two features:

  1. Caching for built output
  2. The ability to override the default handler for .js files, so that you can process all your app’s JS code with Webpack if you so choose.

Of course, if you wait long enough I’m 100% sure someone will build a Webpack package anyway.

1 Like

A build plugin almost seems like overkill since it’s possible to run Webpack independently and have it output its bundles into the Meteor directory where meteor will automatically pick them up. Though it would simplify the launch process a bit. The only thing lacking is for Meteor to pull in the source maps that Webpack generated – seems like that would be trivial for Meteor to do? But thanks for the link, maybe I’ll be able to adapt that code to handle the source maps.

I think that’s the whole point of build plugins. For example, you could run the less compiler separately but it makes more sense as a build plugin so that you can just add a package instead of running a separate tool.

1 Like

@sashko -
Yeah, build plugin sounds like it may be a perfect solution to this. Any chance you could provide a link for the repo of the jsx package? I’ve looked all around, and can’t seem to find it (except for the package on Atmosphere; which has no GitHub link).

@fabienhuet - I’m not saying that there won’t need to be manual analysis or scrubbing of data. I’m just looking for tools that will help in the process. Most of my code is already organized by components; and pretty lean; so all I really need to do is convert

    <template name='Foo'>
 	<p>my text</p>
	<button>
          {{#if foo}}
	    <label>{{foo}}</label>
	  {{/if}}
          words
	</button>
        baz
    </template>

into something like this:

var { dom } = require("deku");

var foo =  
  <p>my text</p>
  <button>
    {{#if foo}}
      <label>{[foo]}</label>
    {{/if}}
    words
  </button>
  baz;

Ideally, the build plugin would know how to wrap a .jsx file in a var, and attach the necessary requires (no clue what this deku thing is, whether it’s a dependency, or just an example. It has no relevancy to the apps I’m writing, other than apparently being a dependency of React; so it should go in the build plugin, rather than cluttering my app code.

var { dom } = require("deku");
var foo =  

;
```
And ideally we'd be left with the following code in a ``foo.jsx`` file:

````html
  <p>my text</p>
  <button>
    {{#if foo}}
      <label>{[foo]}</label>
    {{/if}}
    words
  </button>
  baz
```

Which looks almost exactly like regular Spacebars HTML.  If I know I can convert my HTML into JSX, and expose it to my JS file; that's a huge win, and gets me a considerable step closer to converting existing Blaze apps into React.  I can write utilities that convert file extensions from ``.html`` to ``.jsx`` and replace ``{{ }}`` instances in the code into ``{[ ]}``.  That's doable.

@jedwards - Thanks for the reference for Webpack!  It took me a day or two, and a couple re-reads, to figure out exactly what it's doing; but dang, it's quite the handy little tool, isn't it?  Do you have any additional resources for using Webpack and Meteor?  Any example repos maybe?

@sashko - nevermind; just found the jsx package. jsx-templating had a link.

Also, this jsx-templating package looks great. It’s spot on. timbrandin is on exactly the right track. If folks can get the Blaze API to layer over React, that would be simply amazing. Scrap the different sections in the tutorial (which is confusing), and just have Blaze compile down to React. Then, all of refactoring and migrations become a non-issue.

+1 for what’s going on within jsx-templating

I’m trying to make a plugin. So far, I’m not sure how well it will work. For one, webpack config is specified in a Node js file, meaning it can use require, which is unavailable to my plugin (come on! the server code is running in Node anyway, hiding require from it is just mean). Secondly, the Webpack API is async – it looks like source plugins probably have to be synchronous?

Unfortunately all I’ve found about using the two together was a project that uses yet another frontend framework with Meteor: https://github.com/vuejs/vue-webpack-meteor-example I’m trying to figure out if I can make a Meteor plugin for it but…Meteor makes it quite difficult.

I think I’m just going to make a simple plugin that loads the webpack bundle and its source map. Meteor should really be loading source maps by default though.

1 Like

Arggggggh…

I wrote this simple plugin:

var fs = Npm.require('fs');
var path = Npm.require('path');

function processFile(step) {
  var options = {
    path: step.inputPath,
    data: fs.readFileSync(step.fullInputPath, {encoding: 'utf8'}),
    sourcePath: step.inputPath,
  };
  if (fs.existsSync(step.fullInputPath + '.map')) {
    options.sourceMap = fs.readFileSync(step.fullInputPath + '.map', {encoding: 'utf8'});
  }

  console.log('adding file: ' + options);

  step.addJavaScript(options);
}

Plugin.registerSourceHandler('bundle.js', processFile);

It totally works when loading just the bundle, but blows up when loading the source map: any tips?

/home/andy/.meteor/packages/meteor-tool/.1.1.3.4sddkj++os.linux.x86_64+web.browser+web.cordova/mt-os.linux.x86_64/dev_bundle/lib/node_modules/fibers/future.js:245
throw(ex);
^
Error: “webpack:/webpack/bootstrap e0fa346f18878abd575b” is not in the SourceMap.
at SourceMapConsumer_sourceContentFor [as sourceContentFor] (/home/andy/.meteor/packages/meteor-tool/.1.1.3.4sddkj++os.linux.x86_64+web.browser+web.cordova/mt-os.linux.x86_64/dev_bundle/lib/node_modules/source-map/lib/source-map/source-map-consumer.js:380:13)