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

rewire-webpack doesn't work with babel-loader #12

Open
ryardley opened this issue Apr 20, 2015 · 21 comments
Open

rewire-webpack doesn't work with babel-loader #12

ryardley opened this issue Apr 20, 2015 · 21 comments

Comments

@ryardley
Copy link

@ryardley ryardley commented Apr 20, 2015

CommonJS syntax works

Currently I have the following karma config:

var webpack = require('webpack');
var RewirePlugin = require('rewire-webpack');
var rewirePlugin = new RewirePlugin();

module.exports = function (config) {

  config.set({
    files: ['mjs/my_test_rewire.js'],

    browsers: ['PhantomJS'],
    frameworks: ['jasmine'],

    preprocessors: {
      'mjs/my_test_rewire.js': ['webpack', 'sourcemap']
    },

    webpack: {
      resolve: {
        extensions: ['', '.js', '.jsx']
      },
      module: {
        loaders: [
          { test: /\.(js|jsx)$/, exclude: /node_modules/,
            loader: require.resolve('babel-loader') + '?optional=runtime' },
        ]
      },
      devtool: 'inline-source-map',
      plugins: [ rewirePlugin ]
    }
  });
};

Running the following karma test:

// my_test_rewire.js
var foo = require('./foo');
var rewire = require('rewire');

var fooRewired = rewire('./foo');
fooRewired.__set__('barText', 'dong!');

describe('Rewire test', function() {
  it('should say bar!', function() {
    expect(fooRewired.doBar()).toEqual('dong!');
  });
});

The test pulls in the following dependency:

// foo.js

var barText = require('./bar');

var foo = {
  doBar:function(){
    return barText;
  }
}

module.exports = foo;

Which pulls in another dependency to be mocked:

// bar.js
module.exports = 'bar!';

Which all works like a charm.

ES6 syntax does not

The problem is our source code is all transpiled using ES6 and ES6 modules. So the following does not work and causes an error:

//my_test_rewire.js

import 'es5-shim';
import 'jasmine-jquery/lib/jasmine-jquery';

import foo from './foo';
import rewire from 'rewire';

var fooRewired = rewire('./foo');
fooRewired.__set__('barText', 'dong!');

describe('Rewire test', function() {
  it('should say bar!', function() {
    expect(fooRewired.doBar()).toEqual('dong!');
  });
});
// foo.js
import barText from './bar';

var foo = {
  doBar:function(){
    return barText;
  }
}

export default foo;
// bar.js
export default 'bar!';

The error returned is:

PhantomJS 1.9.8 (Mac OS X) ERROR
  TypeError: 'undefined' is not an object (evaluating '__webpack_require__.m[module].call')
  at /Users/rudi/teg/teg-site/mjs/my_test_rewire.js:125:0 <- webpack:///~/rewire-webpack/lib/rewire.web.js:10:0
PhantomJS 1.9.8 (Mac OS X): Executed 0 of 0 ERROR (0.142 secs / 0 secs)

Even if the test remains in commonJS syntax the test runs but the rewiring fails. Are there any work arounds here at all until the point at which rewire will work with ES6?

@doctyper
Copy link

@doctyper doctyper commented Apr 20, 2015

Just wanted to chime in here, experiencing the same issue. ES6 was working just fine with babel@<5.0. However upgrading to babel@^5.0 broke this functionality.

@virajsanghvi
Copy link

@virajsanghvi virajsanghvi commented Apr 21, 2015

Also experiencing the same issue, with the same setup. Does anyone have any workarounds outside of downgrading babel? Seems like not using import statements works, and other ES6 syntax is fine.

@jhnns
Copy link
Owner

@jhnns jhnns commented Apr 22, 2015

Mhmm ... I don't know much about babel, but rewire is using the given module loader to inject these setters, so as long as they don't mess with the module loader there should be no problem. Could you find out which variable is undefined?

@doctyper
Copy link

@doctyper doctyper commented Apr 22, 2015

From my debugging, the problem comes from this line:
https://github.com/jhnns/rewire-webpack/blob/master/lib/rewire.web.js#L10

In older versions of Babel, the value of module is the corresponding Webpack module id (i.e., if you import jquery, and Webpack maps that to an id of 5, then module = 5, and __webpack_modules__[module] becomes __webpack_modules__[5].

In Babel 5, the value of module is jquery, and __webpack_modules__[module] becomes __webpack_modules__["jquery"], which is undefined:

'undefined' is not an object (evaluating '__webpack_require__.m[module].call')

Best guess is that somewhere import statements are no longer being converted to Webpack ids. Not sure if this is a Babel thing, a Webpack thing, or a Rewire thing. But I'm not seeing this issue with any other plugin.

@jhnns
Copy link
Owner

@jhnns jhnns commented Apr 22, 2015

So, does it work if you deactivate the babel compiler for all modules within rewire-webpack? They're plain old ES5 anyway :)

@doctyper
Copy link

@doctyper doctyper commented Apr 22, 2015

Babel doesn't touch node_modules or rewire-webpack. Not in my environment anyway.

@damassi
Copy link

@damassi damassi commented Apr 22, 2015

See jhnns/rewire#55 as well.

@sairion
Copy link

@sairion sairion commented Apr 24, 2015

Welp, what I'm experiencing is exactly same as what people saying. (I'm using karma+mocha+webpack+rewire+sinon) I just switched most of my codebase to ES6 module syntax and a few tests using webpack-rewire are failin' ... Is there anyone have duktape'd this problem?

@sairion
Copy link

@sairion sairion commented Apr 24, 2015

I found where the problems comes from. Babel and rewire-webpack both actually causing the issue... There is two problems (caused by Babel > 5 's new module name transform rule). First one is importing rewire with module syntax, and second one is __set__ing (or other equiv api) es6-fied module.

First one:

import rewire from 'rewire'
    describe('test-1', function () {
        var fooModule = rewire('../foo-module');

gets transformed into

var _rewire = __webpack_require__(456);
var _rewire2 = _interopRequireWildcard(_rewire);

    describe('test-1', function () {
        var fooModule = _rewire2['default']('../foo-module');

You see the problem. ../foo-module didn't get transformed into proper Webpack module index (number) because Babel transformed rewire call with _interopRequireWildcard()'d rewire.

Second one, Rewire-webpack assumes modules to be declared within module scope as same varname (if import foomodule from 'foomodule' then rewire-webpack expects foomodule to be available in module)

When rewiring, rewire-webpack prints module and rewiring declaration (__set__, __get__)into another module. However, (I assume this behavior comes from Babel > 5) transforms module names get prefixed. like _foomodule, _foomodule2, etc. So foomodule won't be available in module level in this case.

Well, this is pretty much what I've found out so far, but not sure who's responsible to fix this. ;) I will also present this issue to Babel GH issues to see what is their response.

@damassi
Copy link

@damassi damassi commented Apr 24, 2015

Thanks for tracking that down @sairion! Yeah, I just started noticing how Babel 5 is rewriting var form for quite a few things.

@srph
Copy link

@srph srph commented Apr 25, 2015

Awesome, @sairion 👍. I'm looking forward to any progress. Will see if I can help out too 😄.

Just a question, would plain CommonJS require be a good work around for the issue? I'm using Babel as well, but I still use the CommonJS syntax for some libraries (compatibility reasons) in very few cases.

@jhnns
Copy link
Owner

@jhnns jhnns commented Apr 27, 2015

Wow, that's tricky. Thx for investigating @sairion, that's really a big help!

@jhnns jhnns changed the title Support ES6 syntax rewire-webpack doesn't work with babel-loader Apr 27, 2015
@jhnns
Copy link
Owner

@jhnns jhnns commented Apr 27, 2015

rewire and rewire-webpack are both affected. We could probably do something about the first problem (e.g. parsing the module when the rewire-loader is applied), but the second one is imho impossible to solve on my side.

@wbinnssmith
Copy link

@wbinnssmith wbinnssmith commented May 15, 2015

If it helps, I've also set up a small repository demoing the conflict. Just run webpack and load the resulting bundle in a script tag to see the issue.

@bradbarrow
Copy link

@bradbarrow bradbarrow commented Jun 18, 2015

👍 What the latest on this? Encountering the same thing with webpack/karma/webpack-rewire

@damassi
Copy link

@damassi damassi commented Jun 18, 2015

@bradbarrow, this is the suggested alternative: https://github.com/speedskater/babel-plugin-rewire

@0x80
Copy link

@0x80 0x80 commented Aug 6, 2015

The suggested alternative is not working for me. See speedskater/babel-plugin-rewire#28. Did any of you encounter this before?

@ufocoder
Copy link

@ufocoder ufocoder commented Nov 28, 2015

+1

robertknight added a commit to robertknight/react-testing that referenced this issue Jan 10, 2016
In order for rewire calls to be correctly handled like
require() calls by Webpack, rewire currently needs
to be imported using CommonJS style syntax.

See jhnns/rewire-webpack#12
for a discussion and explanation.
@indianscout
Copy link

@indianscout indianscout commented Feb 23, 2016

Any news on this so far?

I'm having the same issue with babel-plugin-rewire.
A possible workaround is assigning the imported module to a variable:

import React from 'react';
import { defineMessages, injectIntl } from 'react-intl';

import LinkListPopover from 'elements/LinkListPopover';
...js

const LinkListPopoveR = LinkListPopover;

class NotificationPopover extends Component {
    static propTypes = propTypes;

    render() {
        const { formatMessage } = this.props.intl;

        const unread = filterTruthyValues(this.props.items, 'unread');

        const emptyState = {
            icon: 'check',
            text: formatMessage({ id: 'notificationPopoverEmpty' })
        };

        return (
            <LinkListPopoveR
...

Attention: but this only works when index.js imports the module in curly brackets like:

import { NotificationPopover } from './NotificationPopover';
export default NotificationPopover;

Removing {} causes the rewire to fail.

Here my deps:

    "babel-loader": "6.2.1",
    "babel-plugin-add-module-exports": "0.1.2",
    "babel-plugin-rewire": "1.0.0-beta-2",
    "babel-plugin-react-intl": "2.1.1",
    "babel-plugin-transform-class-properties": "6.4.0",
    "babel-plugin-transform-object-rest-spread": "6.3.13",
    "babel-polyfill": "6.3.14",
    "babel-preset-es2015": "6.3.13",
    "babel-preset-react": "6.3.13",
    "babel-register": "6.4.3",
    "webpack": "1.12.12",
    "webpack-dev-server": "1.14.1"
...
@jhnns
Copy link
Owner

@jhnns jhnns commented Feb 26, 2016

I'm sorry, I don't know. ES2015 module have some new challenges, like immutable live-bindings, and since rewire is tightly coupled to the module system, it's not easy to find a good solution for this. The situation gets even more complicated since there is no JS engine yet that implements native ES2015 modules. So, a possible solution would be a workaround for the babel transpiler only. But I would like to find a solution, that will also work with native ES2015 modules.

@munnerz
Copy link

@munnerz munnerz commented Jun 10, 2016

I'm experiencing the same issue using TypeScript as a loader in webpack:

TypeError: undefined is not an object (evaluating '__webpack_require__.m[module].call')

It does not work with either ES6 or require syntax (using import xyz = require('./xyz')).

Any progress? I'm unsure how else I can mock my dependencies. I'm using webpack+karma+mocha+typescript (ts-loader), all the latest versions available in npm as of today.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
You can’t perform that action at this time.