speedskater / babel-plugin-rewire

A babel plugin adding the ability to rewire module dependencies. This enables to mock modules for testing purposes.
842 stars 90 forks source link

Missing original value for import #176

Open SevInf opened 7 years ago

SevInf commented 7 years ago

Hello! Thanks for the work on this plugin, we've been successfully using it for more than a year. However, recently we had a problem with it.

Here is the simplified example:

import toString from 'lodash/toString';

export function a(arg) {
    return toString(arg);

}
export function b(arg) {
    return a(arg);
}

Compiling with babel cli:

babel --no-babelrc --compact=false --plugins rewire 

This generates the following code:

import toString from 'lodash/toString';

export function a(arg) {
    return _get__('toString')(arg);
}
export function b(arg) {
    return _get__('a')(arg);
}

var _RewiredData__ = Object.create(null);

var INTENTIONAL_UNDEFINED = '__INTENTIONAL_UNDEFINED__';
let _RewireAPI__ = {};

(function () {
    function addPropertyToAPIObject(name, value) {
        Object.defineProperty(_RewireAPI__, name, {
            value: value,
            enumerable: false,
            configurable: true
        });
    }

    addPropertyToAPIObject('__get__', _get__);
    addPropertyToAPIObject('__GetDependency__', _get__);
    addPropertyToAPIObject('__Rewire__', _set__);
    addPropertyToAPIObject('__set__', _set__);
    addPropertyToAPIObject('__reset__', _reset__);
    addPropertyToAPIObject('__ResetDependency__', _reset__);
    addPropertyToAPIObject('__with__', _with__);
})();

function _get__(variableName) {
    if (_RewiredData__ === undefined || _RewiredData__[variableName] === undefined) {
        return _get_original__(variableName);
    } else {
        var value = _RewiredData__[variableName];

        if (value === INTENTIONAL_UNDEFINED) {
            return undefined;
        } else {
            return value;
        }
    }
}

function _get_original__(variableName) {
    switch (variableName) {
        case 'a':
            return a;
    }

    return undefined;
}

function _assign__(variableName, value) {
    if (_RewiredData__ === undefined || _RewiredData__[variableName] === undefined) {
        return _set_original__(variableName, value);
    } else {
        return _RewiredData__[variableName] = value;
    }
}

function _set_original__(variableName, _value) {
    switch (variableName) {}

    return undefined;
}

function _update_operation__(operation, variableName, prefix) {
    var oldValue = _get__(variableName);

    var newValue = operation === '++' ? oldValue + 1 : oldValue - 1;

    _assign__(variableName, newValue);

    return prefix ? newValue : oldValue;
}

function _set__(variableName, value) { if (typeof variableName === 'object') { Object.keys(variableName).forEach(function (name) {
            _RewiredData__[name] = variableName[name];
        });
    } else {
        if (value === undefined) {
            _RewiredData__[variableName] = INTENTIONAL_UNDEFINED;
        } else {
            _RewiredData__[variableName] = value;
        }

        return function () {
            _reset__(variableName);
        };
    }
}

function _reset__(variableName) {
    delete _RewiredData__[variableName];
}

function _with__(object) {
    var rewiredVariableNames = Object.keys(object);
    var previousValues = {};

    function reset() {
        rewiredVariableNames.forEach(function (variableName) {
            _RewiredData__[variableName] = previousValues[variableName];
        });
    }

    return function (callback) {
        rewiredVariableNames.forEach(function (variableName) {
            previousValues[variableName] = _RewiredData__[variableName];
            _RewiredData__[variableName] = object[variableName];
        });
        let result = callback();

        if (!!result && typeof result.then == 'function') {
            result.then(reset).catch(reset);
        } else {
            reset();
        }

        return result;
    };
}

export { _get__ as __get__, _get__ as __GetDependency__, _set__ as __Rewire__, _set__ as __set__, _reset__ as __ResetDependency__, _RewireAPI__ as __RewireAPI__ };
export default _RewireAPI__;

As you can see, _get_original__ has no case for toString import. This causes function b to fail with undefined is not a function error.

speedskater commented 7 years ago

@SevInf Thanks for reporting this issue and sorry for the late reply. The issue will be tackled, but due to a thight schedule this won't happen before mid/end of march. Is it a possible workaround for the while being to rewrite the problematic code like this:

function a(arg) {
    return toString(arg);
}
export function a;
dzek69 commented 7 years ago

I just came into similiar/same issue I think. This causes problems with external package called must in my tests, so I cannot just update problematic code.

Any chances for this to be resolved?