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

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

Comments

Projects
None yet
@ryardley

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

This comment has been minimized.

Show comment
Hide comment
@doctyper

doctyper 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.

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

This comment has been minimized.

Show comment
Hide comment
@virajsanghvi

virajsanghvi 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.

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

This comment has been minimized.

Show comment
Hide comment
@jhnns

jhnns Apr 22, 2015

Owner

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?

Owner

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

This comment has been minimized.

Show comment
Hide comment
@doctyper

doctyper 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.

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

This comment has been minimized.

Show comment
Hide comment
@jhnns

jhnns Apr 22, 2015

Owner

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

Owner

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

This comment has been minimized.

Show comment
Hide comment
@doctyper

doctyper Apr 22, 2015

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

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

@damassi

This comment has been minimized.

Show comment
Hide comment

damassi commented Apr 22, 2015

See jhnns/rewire#55 as well.

@sairion

This comment has been minimized.

Show comment
Hide comment
@sairion

sairion 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 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

This comment has been minimized.

Show comment
Hide comment
@sairion

sairion 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.

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

This comment has been minimized.

Show comment
Hide comment
@damassi

damassi 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.

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

This comment has been minimized.

Show comment
Hide comment
@srph

srph 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.

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 jhnns referenced this issue in jhnns/rewire Apr 27, 2015

Closed

rewire doesn't work with babel #55

@jhnns

This comment has been minimized.

Show comment
Hide comment
@jhnns

jhnns Apr 27, 2015

Owner

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

Owner

jhnns commented Apr 27, 2015

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

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

@jhnns

This comment has been minimized.

Show comment
Hide comment
@jhnns

jhnns Apr 27, 2015

Owner

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.

Owner

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

This comment has been minimized.

Show comment
Hide comment
@wbinnssmith

wbinnssmith 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.

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.

@zhon zhon referenced this issue in allan-stewart/react-hogwarts-tdd-kata May 28, 2015

Closed

Create build system #3

@bradbarrow

This comment has been minimized.

Show comment
Hide comment
@bradbarrow

bradbarrow Jun 18, 2015

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

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

@damassi

This comment has been minimized.

Show comment
Hide comment

damassi commented Jun 18, 2015

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

@jhnns jhnns referenced this issue in jhnns/rewire Jun 24, 2015

Open

rewire does not work with babel #62

@eguneys eguneys referenced this issue in badsyntax/react-seed Jul 17, 2015

Open

How to mock React components? #18

@0x80

This comment has been minimized.

Show comment
Hide comment
@0x80

0x80 Aug 6, 2015

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

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

This comment has been minimized.

Show comment
Hide comment

+1

robertknight added a commit to robertknight/react-testing that referenced this issue Jan 10, 2016

Use CommonJS-style requires for rewire
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

This comment has been minimized.

Show comment
Hide comment
@indianscout

indianscout 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"
...

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

This comment has been minimized.

Show comment
Hide comment
@jhnns

jhnns Feb 26, 2016

Owner

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.

Owner

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

This comment has been minimized.

Show comment
Hide comment
@munnerz

munnerz 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.

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