petehunt / node-jsx

transparently require() jsx from within node
164 stars 32 forks source link

Compatibility with webpack for server-side-rendering #29

Closed benoneal closed 9 years ago

benoneal commented 9 years ago

I'm creating an isomorphic React app with Express4 and Webpack, and I'm trying to include css files using the Webpack method of requiring them in my components (as per this loader). Unfortunately, on the server-side, node-jsx is attempting to transform the components, and throws an error if they include css:

Error: Error transforming [component/style.css] to JSX: Error: Parse Error: Line 1: Unexpected token .

This is a real pain in the butt.

benoneal commented 9 years ago

Just confirmed that there is also a conflict with other modules when attempting to render on the server. It errors with react-fa (font-awesome component module) with the following error:

Error transforming [app_path]/node_modules/react-fa/node_modules/font-awesome/css/font-awesome.css to JSX: Error: Parse Error: Line 7: Unexpected token ILLEGAL
0nn0 commented 9 years ago

Running into the same problem.

gpbl commented 9 years ago

You can't just require a css in node, eventually you need to build the node part with webpack (or alternatives).

An easier solution is to require those components only client side, ie adding if BROWSER require(...), and use them after the component has been mounted.

I would just use components working on both the environments: a component for font awesome sounds useless imho :)

rmarscher commented 9 years ago

I've had some luck using if (process.pid) { } to determine if the code is executing on the server or client. Then only require files processed by the webpack loaders if executing on the client. Similarly, you can decide whether to use render() or renderToString().

A webpack loader to remove requiring node-jsx could be useful. Something like:

module.exports = function(source) {
    this.cacheable();
    return source.replace(/require\(\s*'node-jsx'\s*\).install\(.*?\);\n/, "");
};
benoneal commented 9 years ago

Requiring css only on the client side defeats the purpose of an isomorphic app. I need to be able to process the css during server-render, so that the page can be delivered with css. I'd like to be able to take advantage of webpack's module bundling so that css is bundled with React components that require them, but as long as node-jsx errors on css files, all my styles have to be managed with a separate (gulp) pipeline, completely defeating the modular approach.

gpbl commented 9 years ago

@benoneal node itself (not node-jsx) does not accept a CSS. Requiring CSS on the client is intended as "skip requiring css on the server, but deliver them on the client with webpack" . You can indeed require a single CSS in your JSX files and deliver a bundled .css as usual: you don't need gulp at all. Use the webpack's ExtractTextPlugin.

// a jsx file
render() {
  if (BROWSER) require('component.css')
  return <div>Hi</div>
}

// your webpack config
module: {
loaders: [
  // will intercept all .css requires
  { test: /\.css$/, loader: ExtractTextPlugin.extract("css-loader") }
]
},
  plugins: [
    // create a bundled.css to use in your html source
    new ExtractTextPlugin("bundled.css"),
}
todsul commented 9 years ago

@benoneal I'm working on the same problem with an isomorphic app, so would be interested in your final solution. The problem with having some styles in a standard .css file and other styles required by the client app is the FOUC before the client styles load. So really it seems all styles need to be in a .css.

This is a similar issue with state. With SSR, I'm finding that not only do I need identical states on client and server (to avoid a mismatch and another FOUC), but the only way to sync them is by essentially duplicating state in the server response (via a script tag, dataset or whatever). That's easy enough with React, but with Flux it's getting a little messy. I've seen that Yahoo and others have worked it out, but we're having fun experimenting with our own somewhat simpler solutions.

voronianski commented 9 years ago

@benoneal I have the same problem (the only difference that I'm using babel instead of node-jsx). Any workarounds or step by step guide?

maletor commented 9 years ago

I'm interested in the semantic solution here too. I've seen projects override require to ignore png and CSS extensions only to be filled in later by client side code.

voronianski commented 9 years ago

@maletor yeah require.extensions['.css'] is the quickest.

maletor commented 9 years ago

It's not as bad of a problem as I originally thought, since this can be isolated to development and factored into a production build.

It's still an issue, but I'm not sure it will be easy to fix at all.

maletor commented 9 years ago

require.extensions is deprecated too.

maletor commented 9 years ago

So I missed this in the huge documentation that is webpack but here's our answer!

http://stackoverflow.com/a/29358828/381285

frankleng commented 9 years ago

@maletor not sure if that would solve the problem, since that example isn't an isomorphic build. unless I'm missing something...

ftorre104 commented 9 years ago

To expand on maletor's answer, the idea is to use webpack to create 2 bundles -- one for the client and one for your server.

You still have your single source code -- one code-base. Currently you may be creating a bundle of that code, bundle.js, to send to the client; and you run your node server directly on that code-base.

To use webpack on the server, the idea is to create 2 separate bundle files from your single code base. It requires 2 separate webpack configurations, and 2 separate builds. Your original source code will no longer run your server directly.

One webpack target will be for the client. Something like (psuedocode): target: web, entry: app.js, extensions: [scss, jsx, coffee, etc], output: client.bundle.js

The other webpack config target will be for your server: target: node, entry: server.js, extensions: [jsx, coffee, etc], plugin: [ignore css], output: server.bundle.js

The above is just meant to illustrate the distinction.

The client side will load client.bundle.js in the browser. Your server, instead of running on your normal server.js, will now run on your built server.bundle.js.

This allows you to use the power of webpack -- feature flags, enhanced requires, loaders, chunks, etc -- on the server.

maletor commented 9 years ago

For what it's worth, this issue has caused me to shift gears and look at the solution Ember.js has found. With Ember 2.0 bringing all the good parts of React on board, I think it's a much better solution overall. There is way too too much time spent trying to configure React and WebPack.

On Mon, May 11, 2015 at 11:06 AM Fede Torre Mtz notifications@github.com wrote:

To expand on maletor's answer, the idea is to use webpack to create 2 bundles -- one for the client and one for your server.

You still have your single source code -- one code-base. Currently you may be creating a bundle of that code, bundle.js, to send to the client; and you run your node server directly on that code-base.

To use webpack on the server, the idea is to create 2 separate bundle files from your single code base. It requires 2 separate webpack configurations, and 2 separate builds. Your original source code will no longer run your server directly.

One webpack target will be for the client. Something like (psuedocode): target: web, entry: app.js, extensions: [scss, jsx, coffee, etc], output: client.bundle.js

The other webpack config target will be for your server: target: node, entry: server.js, extensions: [jsx, coffee, etc], plugin: [ignore css], output: server.bundle.js

The above is just meant to illustrate the distinction.

The client side will load client.bundle.js in the browser. Your server, instead of running on your normal server.js, will now run on your built server.bundle.js.

This allows you to use the power of webpack -- feature flags, enhanced requires, loaders, chunks, etc -- on the server.

— Reply to this email directly or view it on GitHub https://github.com/petehunt/node-jsx/issues/29#issuecomment-101001252.

veeracs commented 9 years ago

I'm going through the same pain!

In Home.jsx I have import '../../client/styles/home.scss';

babel-node index.js

/Users/cveera/Sites/hapi-fluxible-webpack/node_modules/node-jsx/index.js:27 throw new Error('Error transforming ' + filename + ' to JS: ' + e.toStri ^ Error: Error transforming /Users/cveera/Sites/hapi-fluxible-webpack/lib/app/views/Home.jsx to JS: Error: Parse Error: Line 3: Illegal import declaration at Object.require.extensions.(anonymous function) as .jsx at Module.load (module.js:356:32) at Function.Module._load (module.js:312:12) at Module.require (module.js:364:17) at require (module.js:380:17) at Object. (/Users/cveera/Sites/hapi-fluxible-webpack/lib/app/views/Routes.jsx:11:15) at Module._compile (module.js:456:26) at Object.require.extensions.(anonymous function) as .jsx at Module.load (module.js:356:32) at Function.Module._load (module.js:312:12)

petehunt commented 9 years ago

You should use https://github.com/petehunt/webpack-require which does exactly what you want, not node-jsx.

hzhu commented 8 years ago

Can anyone link to a repo using webpack-require? Running into some issues, would like to see it in action, but can't locate a project using it.

stevus commented 7 years ago

@petehunt you closed this issue, however webpack-require is only in beta. Would not consider this issue closed as a result until it is not beta software.