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

AMD compatibility #171

Closed
andreaferretti opened this issue Aug 6, 2014 · 20 comments
Closed

AMD compatibility #171

andreaferretti opened this issue Aug 6, 2014 · 20 comments

Comments

@andreaferretti
Copy link

Is there a way to use react-router as an AMD module? I have tried to pull it with requirejs, but I get TypeError: React is undefined. It seems that it tries to refer to a React variable in the global scope.

I understand that one can use react-router with browserify, but there are many situations where browserify is not enough (requirejs handles dependencies of resources other js, dynamic loading, shimming components that are not AMD and more).

@ryanflorence
Copy link
Member

Shim it like a global.

require.config({
  shim: {
    'path/to/react-router-dist-build': {
      deps: ['react'], // <-- whatever your react path is
      exports: 'ReactRouter'
    }
  }
});

@spoike
Copy link

spoike commented Aug 20, 2014

Still not working. And I'm using this shim config (react and react-router paths are set correctly because both are loaded):

    'shim': {
        'react': {
            exports: 'React'
        },
        'react-router': {
            deps: ['react'],
            exports: 'ReactRouter'
        }
        /* ... */
    }

Even though deps has react specified in the shim config, react-router crashes because it attempts to use React from the global scope (i.e. window.React). React correctly doesn't leak to the global namespace when using requirejs.

I'm using the bower packages for both react and react-router.

Current workaround is to expose React to the global scope in your bootstrapping code before using ReactRouter:

require(['react'], function(React) {
    window.React = React;
});

@ryanflorence
Copy link
Member

Yeah, this is a mess made by UMD and browserify and the JS module ridiculousness in general. We're not exactly excited about shipping anything but CJS, so trying to get these kinds of things upstream to browserify and react or others is even less exciting.

We use react and the router in an AMD app. I make a module at <baseUrl>/react.js that pulls in the shimmed bower installed version, and then exposes to window, then we require(['react'] ...) and it brings in this adapter module.

define(['bower_components/react/react'], function(React) {
  window.React = React;
  return React;
});

@andreaferretti
Copy link
Author

Well, of course this would work, but it is less than ideal - in the end the whole point about AMD and CommonJS is to handle dependencies without having to mess with globals.

I think you should be able to generate ADM-compatible builds using uRequire. I do something similar in Paths.js, although in my case I write AMD and generate CommonJS modules.

@ryanflorence
Copy link
Member

@andreaferretti I have fought the AMD/CJS/UMD battle for 3 years. I'm done. We ship CJS and Globals. Our globals are built with browserify's standalone wrapper that makes it seem like AMD should work, but it doesn't.

As long as you can hack your loader to work (and you can) then that's all I care about :)

@leoselig
Copy link

This might be the most ridiculous issue response I've ever seen.
"I have fought the AMD/CJS/UMD battle for 3 years. I'm done."
This is by far the best routing implementation for React out there and it is - from a software quality point of view - unusable for a lot of projects because the publisher is - justifiably - too annoyed to implement a cross-module-system-solution as hundreds of other libraries already did.
by the way: react supports AMD as well, this seems like a clear directive to all plugins to me, doesn't it?

@ryanflorence
Copy link
Member

@leoselig We use the router in an AMD app. The only thing you have to do is make sure that React is on window (which you're going to want anyway for the react dev tools). I absolutely want anybody to be able to use this router and I believe they can.

Putting something like @baseUrl/react.js in your project like this is enough:

define(['bower_components/react/react'], function(React) {
  window.React = React;
  return React;
});

I'm going to give a very long response here in case anybody else is concerned about us not having direct support for AMD.

This might be the most ridiculous issue response I've ever seen

I would encourage you to view more responses, preferably from the linux maintainers :P

too annoyed ...

Not annoyed at all. Just pragmatic at this point. CJS is the module format of today, it won. ES6 modules are the future and I can't wait :)

... to implement a cross-module-system-solution as hundreds of other libraries already did

These hundreds of other libraries don't have third-party dependencies like we do. That's the real trick here, please see my article about that, but maybe finish reading this comment first: http://ryanflorence.com/blog/umd-is-a-lie.html

I should have given a bit more context behind this:

I have fought the AMD/CJS/UMD battle for 3 years. I'm done.

  1. I helped author the UMD format (See my face on the side? https://github.com/umdjs)
  2. I converted a 40,000 line JS project into AMD
  3. I helped with (and even named) the shim config in RequireJS
  4. I became a bower contributor when I was attempting to get some magical module loading with projects like these:
  5. I created the https://github.com/rpflorence/broccoli-dist-es6-module to be able to create libraries that work everywhere
  6. I accidentally killed the globals support in the es6 module transpiler while working on (5)
  7. I have written several ember libraries using the work from (5) which is what led me to give up on trying to support everything directly because, as I discuss in the article linked earlier, you can't have dependencies.

So when I say "I have fought the AMD/CJS/UMD battle for 3 years" I do not say it as an annoyed consumer messing around in my applications, I say it as a library author who worked tirelessly on trying to make it better.

My current approach of direct cjs support, and then a global build, supports every loader I am aware of. If you can't get it to work, I'd happily do a screen hero / google hangout screen share with you sometime to make it work :)

@nnarhinen
Copy link

I agree that in my eyes, commonjs modules have won this round. It's interesting to see what happens when ES6 module support is mainstream.

It is possible of course making additional build steps using amd-wrap and friends to make it work but is it really worth the hassle?

@ryanflorence
Copy link
Member

is it really worth the hassle

I don't think so. Using shim configs with the global build is all anybody needs to do.

@tiye
Copy link

tiye commented Dec 17, 2014

How about in optimizing?

I got react-router work in developing but it throws error when I optimized code into one file(actully two: deps.js main.js).

util/router-helper:

define (require, exports, module) ->
  React = require 'react'

  window.React = React

deps:

define [
  # other packages and
  'react'
  'flux'
  'react-router'
  'util/router-helper'
  ], ->

task in gulpfile.coffee:

gulp.task 'depsjs', ->
  rjs = require 'gulp-requirejs'
  uglify = require 'gulp-uglify'
  rename = require 'gulp-rename'

  rjs
    wrap: false
    preserveLicenseComments: false
    optimize: 'none'
    out: 'deps.js'
    baseUrl: 'build/script/'
    name: 'deps'
    mainConfigFile: 'build/script/config.js'
    include: '../../bower_components/almond/almond.js'
    insertRequire: ['deps']
  # .pipe uglify()
  .pipe rename(suffix: '.min')
  .pipe gulp.dest('./build/')

configs of requirejs:

requirejs.config
  paths:
    # others and
    'react': '../../bower_components/react/react-with-addons'
    'flux': '../../bower_components/flux/dist/Flux'
    'react-router': '../../bower_components/react-router/dist/react-router'
  shim:
    react:
    # others and
      exports: 'React'
    'react-router':
      deps: ['util/router-helper']
      exports: 'ReactRouter'

But I got this error in loading page:
error

and a bit strange, code of react-router is already packaged in code:
code

@gaearon
Copy link
Contributor

gaearon commented Dec 17, 2014

I understand that one can use react-router with browserify, but there are many situations where browserify is not enough (requirejs handles dependencies of resources other js, dynamic loading, shimming components that are not AMD and more).

Sorry for chiming in.

Webpack does all of that, and much better than RequireJS. We switched from RequireJS and never looked back. (And Webpack can consume AMD, CommonJS, you name it, you can mix them in one project and it doesn't care.)

@gaearon
Copy link
Contributor

gaearon commented Dec 17, 2014

The only thing you have to do is make sure that React is on window (which you're going to want anyway for the react dev tools)

This is not entirely correct, React 0.12 works with devtools without being a global (they reversed the hook, so React looks for DevTools instead).

@tiye
Copy link

tiye commented Dec 17, 2014

Some updates on what we found on why optimized code is not working,
in dist/react-router.js there is one line of code:

!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define(        [],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.React=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){

Notice that in that define( [], e) there is not a pacakge name.

else if("function"==typeof define&&define.amd)define(        [],e);

after optimizing, it stays same.

It's interesting for React since we found in optimized code of React, that line of code changed to:

else if("function"==typeof define&&define.amd)define('react', [],e);

quite surprising. Any idea on how it was happening?

@hipertracker
Copy link

I just use ES6 modules. The future is ES6 (http://www.2ality.com/2014/09/es6-modules-final.html). My example project: https://github.com/hipertracker/react-es7

@ryanflorence
Copy link
Member

@jiyinyiyong we've updated our global build to use webpack instead of browserify, which should change the output, do you still have the same problem?

@tiye
Copy link

tiye commented Jan 8, 2015

@rpflorence I'm late.. Thx. I managed to build code based on 0.11.6 with the router-helper.js solution.

@tiye
Copy link

tiye commented Jan 8, 2015

@hipertracker ES6 sounds good. Currently we are writing in CoffeeScript.(JSX and Router in .coffee, a bit stubborn...) I'm quite curious how you debug. Bundling tens of files(our project size, check talk.ai) into one is supposed to be slow, and what do you think about that?

@hipertracker
Copy link

@jiyinyiyong debugging is simple because you have the source maps (the same technique Coffeescript and the others are using). Some errors can be avoided by using ES6 constants. The ES6 transpiler will raise an exception if you change it's value. The bundling is quite fast because Broccoli do all transformations in the memory and it works intelligent, it does not rebuild everything every time. And last , but not least, you do not need to build the final bundle all over the time, but only before you deploy the project.

@tiye
Copy link

tiye commented Jan 8, 2015

@hipertracker Sounds Broccoli is a nice option, I'd like try Broccoli someday.

@gaearon
Copy link
Contributor

gaearon commented Jan 8, 2015

Webpack also has a watch mode, the cool thing is that it works both for dev builds (local dev server) and for production-ish builds (just add watch: true to config and rsync build output in callback).

@lock lock bot locked as resolved and limited conversation to collaborators Jan 25, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants