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

Decorator discussion #102

Open atticoos opened 8 years ago

atticoos commented 8 years ago

This is meant to be a discussion for future support as more ES7 features become standardized.

Currently every import comes bundled with the Rewire API. In order to rewire components, the rewire api is called and provided with the mock.

Decorators as Rewire

import DependencyMocks from './dependencyMocks';

@Rewire({exportedMethod: DependencyMocks.exportedMethod})
import Dependency from '../src/dependency';

This is purely hypothetical, but the idea is to harness decorators and apply the Rewiring capabilities at the time of trasnpilation only when the @Rewire decorator is provided on an object that is/was imported. Based on the rules provided to the decorator, this will compile into the API call.

Dependency.__REWIRE__('exportedMethod', DependencyMocks.exportedMethod)
erykpiast commented 8 years ago

It might work if decorators may be used with import statements. As far as I know they are designed for classes and methods.

I guess it doesn't really matter, it's implementation detail. I think what you are proposing is to move rewiring process to, lets say, "import stage" (opposing to current "post-import" approach). I saw something similar in proxyquire, that works quite nice, however it does not allow repeated rewiring.

speedskater commented 8 years ago

@ajwhite, @erykpiast Thanks for starting the discussion. @ajwhite I think this moves along the directions of the current discussion how to support all es2015 (currently in the babel 6 issue). I think we should be really careful when altering coded. Maybe a comment in the head of the file which should be rewired (as proposed in babel6 bug as well as on bitter should be sufficient). what do you think?

atticoos commented 8 years ago

Maybe a comment in the head of the file which should be rewired (as proposed in babel6 bug as well as on bitter should be sufficient)

Could you link a reference or explain that a little further? I'm not quite sure what you mean by this :blush:

What would the gain be by adding a comment in?

speedskater commented 8 years ago

It would be enough to add something like /* @rewireModuleId=MySpecificModuleId */ at the top of a file which would you like to rewire. After that you would just have to call something like rewire('MySpecificModuleId').set('Dependency', myMockedApi).

atticoos commented 8 years ago

Oh I see. So little flags for the compiler? I'm assuming Babel would pick these up and generate code for component to be rewireable?

elicwhite commented 8 years ago

@ajwhite Yeah, that was one approach we have been considering

erykpiast commented 8 years ago

I prefer approach similar to current one, at least in terms of ability to rewiring module multiple times. It gives me more possibilities of arranging code in my tests.

Proposed identification mechanism looks great, but personally, I don't like custom syntax. If we really need it (I don't see another option so far), maybe it may use some already adapted concepts. I work with Webpack for a while, it extends import system with something calls "loaders". Loader is kind of transformation of required module. For rewire it could look like:

import myModule from 'rewire?id=myModule!../index';

I don't like this syntax too but it looks more clear and explicit for me than some statement hidden in comment.

speedskater commented 8 years ago

I think we need some kind of approach which is independ of the module loader (e.g. webpack or similar)

erykpiast commented 8 years ago

Hm, what I proposed is not to use Webpack loaders, but create custom syntax similar to what people know. Inserting Rewire ID inside module URL makes sense for me. In my opinion it's the most explicit way of declaring such thing, until it's required to place that // @ comment right next to module import statement.

speedskater commented 8 years ago

@erykpiast could create a comment with a small sample outlining your idea?

erykpiast commented 8 years ago

Sure thing!

I have something like this in mind: https://gist.github.com/erykpiast/ddb090a9ee418e768aa6

API similar to Webpack loaders (loader?parameter=value!) is just an example, I guess we can't use it actually until we create actual loader, because Webpack will parse it by itself and try to find that loader.

Anyway, the concept is to identify module directly when importing it and do it in the way compatible with standard ES6 import. Custom syntax for module path/URL seems for me more explicit than custom syntax inside comment, placed in rewired module.

That would be nice, I think, but now I can see serious gap in my reasoning... How rewired module may be connected with ID provided like that?

speedskater commented 8 years ago

Thanks for providing the gist. I like the api in example.spec.js. This is indeed similar to the api i have intended.

Regarding the part in my-module.js. I think this won't workout. As you explained before we do not have the id. The part with Rewire.set I think can be replace by Rewire.set('moduleId', RewireAPI). The approach you sketched in line 12 was the original approach we used in this plugin, but replaced with the custom getters approach. The reason was that this is the only way to rewire constant expression (imports are effectively constant as well). Furthermore by using this custom getters we are able to support cyclic dependencies and live bindings (both part of es2015 spec).

But we could do it in a two leveled approach.

What do you think?

erykpiast commented 8 years ago

Sorry I confused you, code in line 12 was only an example. The idea was to connect rewiring code inside module with ID used in tests.

Your plan makes perfect sense for me. It's good to make small improvements, release new versions and validate ideas fast.

speedskater commented 8 years ago

@erykpiast thats true, but I am limited in my amount of time which i can dedicate to this product. Therefore if you would like to join us, you are welcome :).

erykpiast commented 8 years ago

It may be quite challenging for me, I've never play with code transformations, I guess there is a lot new APIs to learn :) I planned to read about it anyway, maybe I will be able to help with something after that. What resources do you recommend for beginners like me?

elicwhite commented 8 years ago

The JavaScript AST explorer is probably your best bet because it supports the Babel transform apis and you can test things out in your browser.

http://astexplorer.net/

Choose babelv6 from the transform menu.

atticoos commented 8 years ago

That's a great link @TheSavior! Thanks for sharing :smile:

speedskater commented 8 years ago

@erykpiast @ajwhite I would recommend the babel plugin handbook. It explains really well how the involved data structures work together. https://github.com/thejameskyle/babel-handbook/blob/master/translations/en/plugin-handbook.md