asapach / babel-plugin-rewire-exports

Babel plugin for stubbing [ES6, ES2015] module exports
MIT License
66 stars 7 forks source link

Re-exports from within node_modules #22

Closed dirv closed 4 years ago

dirv commented 4 years ago

I'm attempting to rewire exports from within a Node module dependency, for use in my unit tests.

The advice here suggests that for re-exports, I should rewire the export from the original file instead. Unfortunately that's not possible for Node without using a file path for the import rather than the package name.

I had a quick look at the source and I can see why this is happening (see this line) but knowing how to add support for this behavior is totally beyond my ability :)

To give a specific example, the package I'm working with right now is svelte-routing which contains ES6 code only. Its main package file is a list of re-exports from other files: see https://github.com/EmilTholin/svelte-routing/blob/master/src/index.js.

One of these exports is Route. In my unit tests I'd like to be able to write:

import { rewire$Route, restore } from "svelte-routing";

However I have to do this:

import { rewire as rewire$Route, restore } from "./node_modules/svelte-routing/src/Route.svelte";
sergei-startsev commented 4 years ago

The deep import path that starts with the bare path should also work here:

import { rewire as rewire$Route, restore } from "svelte-routing/src/Route.svelte";
asapach commented 4 years ago

I think the problem we ran into when trying to modify re-exports was support for live bindings. For example:

export { foo as bar } from './baz.js'

Could become:

import { foo } from './baz.js'
export var bar = foo;
export rewire$bar($stub) {
  bar = $stub;
}

The modified code makes a couple of assumptions:

If at least one of those assumptions turns out to be wrong, the code will not work as expected.

I would recommend following @sergei-startsev's advice, unless you can suggest an alternative approach.

dirv commented 4 years ago

In this case the deep import works so that's certainly better. Subpaths won't always work though, because if a package defines an exports property in its package.json then Node blocks access to any subpath that isn't explicitly exported. Absolute paths will still work in this case.

I agree it's worth finding a solution that doesn't have unexpected/surprising behavior for certain edge cases, and if there isn't a straightforward solution then the workarounds are absolutely fine. (Fine for me at least... since I'm using this for unit tests I don't have a problem using subpaths or absolute paths, if it gets my tests working.)

I'm no expert on ES6 modules or on AST transforms, but would another potential solution be to re-write the imports, to flatten them out? So instead of:

import { Route, rewire$Route } from "svelte-routing";

You'd get:

import Route, { rewire as rewire$Route} from "./node_modules/svelte-routing/src/Route.svelte";

(This particular example is complicated by the fact that Route is a default export within its original source file.)

sergei-startsev commented 4 years ago

You can always implement a custom babel plugin for that, I don't see benefits though.

asapach commented 4 years ago

One of the limitations of Babel plugins is that they run in relative isolation from the file system -- and in fact it's possible to transform ast completely in memory without any files backing it. It should not care about the file resolution mechanism, be it in Node, webpack, rollup, etc. So, while what you're suggesting is possible, I think it's beyond the scope of this particular plugin.

dirv commented 4 years ago

Okay, that makes sense. Thanks both.