babel-plugin-rewire icon indicating copy to clipboard operation
babel-plugin-rewire copied to clipboard

Decorator discussion

Open atticoos opened this issue 8 years ago • 18 comments

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)

atticoos avatar Feb 28 '16 20:02 atticoos

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.

erykpiast avatar Feb 29 '16 08:02 erykpiast

@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?

speedskater avatar Feb 29 '16 20:02 speedskater

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?

atticoos avatar Feb 29 '16 20:02 atticoos

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

speedskater avatar Feb 29 '16 20:02 speedskater

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?

atticoos avatar Feb 29 '16 20:02 atticoos

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

TheSavior avatar Feb 29 '16 20:02 TheSavior

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.

erykpiast avatar Feb 29 '16 20:02 erykpiast

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

speedskater avatar Feb 29 '16 20:02 speedskater

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.

erykpiast avatar Feb 29 '16 21:02 erykpiast

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

speedskater avatar Mar 06 '16 09:03 speedskater

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?

erykpiast avatar Mar 06 '16 13:03 erykpiast

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.

  • The initial approach would use the id provided in the header. With this we could test the approach and improve our api.
    • Whether underscores are needed or not.
    • Delegate to get or set properties
    • Ist it possible to generate a code mode to transpile existing test code to the new API ....
  • When we have figured out the workflow and API, we can try to find module system specific hooks/loaders which would allow us to be infer the module api from the location.
    • A webpack loader
    • System.js hook (although I am not too familiar with this concept at the moment)

What do you think?

speedskater avatar Mar 06 '16 13:03 speedskater

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.

erykpiast avatar Mar 06 '16 13:03 erykpiast

@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 :).

speedskater avatar Mar 11 '16 08:03 speedskater

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?

erykpiast avatar Mar 11 '16 11:03 erykpiast

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.

TheSavior avatar Mar 11 '16 16:03 TheSavior

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

atticoos avatar Mar 11 '16 17:03 atticoos

@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

speedskater avatar Mar 11 '16 17:03 speedskater