Babel-plugin-module-resolver: Relative paths not correctly resolved when used with babel-register

Created on 22 Jul 2016  Â·  37Comments  Â·  Source: tleunen/babel-plugin-module-resolver

We want to use this plugin because it is really great, but we couldn't easily get it working our serverside-rendering app.

.babelrc

{
  "presets": [
    "es2015-loose", "stage-1", "react"
  ],
  "plugins": [
    [
      "module-alias",
      [
        { "src": "./src/js-modules/modules", "expose": "modules" }
      ]
    ]
  ]
}

services/v8/serverside.js

...
require('babel-register')({
        only: /src\/js-modules/
    });

When we now try to transform a file with import a from 'modules/abc'; we receive an error:

Error: Cannot find module '../../../../../services/v8/src/js-modules/modules/abc'

So it seems that the paths are not relative to the babelrc file, but to something else. Did we make something wrong?

bug

Most helpful comment

Thanks @danez for reporting the issue. I believe we've found the root cause of this issue.
Unfortunately I'm really busy this week and will be out of town this weekend, but I'll be working on a new version of the plugin with hopefully a fix for you next week! I wanted to give you an update on this issue so you know I'm reading the tickets ;)

All 37 comments

Thanks @danez for reporting the issue. I believe we've found the root cause of this issue.
Unfortunately I'm really busy this week and will be out of town this weekend, but I'll be working on a new version of the plugin with hopefully a fix for you next week! I wanted to give you an update on this issue so you know I'm reading the tickets ;)

@tleunen Any status on this bugfix? Happy to help out!

Yes sorry, I tried to find a way but couldn't achieve what I wanted. Basically, inside the plugin, I'd need to know from where the configuration is loaded (ie. find the path of babelrc file), but I don't know how to do it.

Paths inside the module-alias configuration must be relative to the configuration (which is usually the case for the users, but the plugin then transform them based on the working directory (which is wrong). That's why I need to find a way to get the path from the configuration file.

I wonder, could you setup a small project to easily reproduce this error? It would help me a lot :)
Thanks.

I just updated the sample repo again, it now uses version 1.6. I wasn't able to reproduce this issue with 2.0. I'm now gonna try to integrate it again into our codebase that has several uses of the plugin (plain babel compile, webpack, node for server-side rendering and tests). I report back later.

Ok I was finally able to reproduce. Once again this is the repo that now yields an errror. https://github.com/danez/babel-plugin-module-resolver-issue-61

Hey @danez, thank you very much for the test repo. Indeed, I was able to easily reproduce the issue, and I believe, as you may also noticed, the current implementation of the plugin has a "small" limitation.

The plugin calculates the path based on the working directory, so if your babelrc is at the root of the project, that's ok and you specify the paths (root/alias) based on that. But this also means if you run babel node or the babel register in a node environment, you have to run it at the root of your project.

So instead of writing

cd ./node/service; BABEL_DISABLE_CACHE=1 node index.js

Use this:

BABEL_DISABLE_CACHE=1 node ./node/service/index.js

I'm hoping to change this "working directory" behavior but at the moment, I haven't found a good way to know the path/source of the babelrc file when calculating the paths.

Yes the call for node was just for illustration. In our real environment pm2 is starting up the node service and I'm not entirely sure how or if it changes the cwd.

I thought about searching for the closest babelrc, but this would probably not work if people have their config in package.json, webpack or supplying it directly to babel-register.

I have a way to find the closest configuration file (either babelrc/package.json using find-babel-config) but running it for every imports/require seems wrong. Especially if babel already has the configuration somewhere.
That's why I created this ticket on the Babel side to hopefully have a way to know the source of the configuration.

The thing I was hoping for is to know the file path of the configuration, but if it comes from babel-register, then I'd need to fallback on something, like the working directory.

Is there any workaround for this issue for now? Btw thank you for an awesome plugin 😊

I'm thinking of introducing a global environment variable allowing to setup the "working directory" for the plugin. Do you think it could resolve your issue?

For pm2, have you read this? https://github.com/Unitech/pm2/issues/96

So there is no way to find path of .babelrc once and then use it as a global variable for absolute imports?

Maybe when the file is "executed". I'd need to investigate and try a few things here :) Especially if there're multiple babelrc files in a project, I'd need to know if the configuration is retrieved when the node process is executed, or on a per-file basis. But I'm guessing it's only when babel starts.. Otherwise it would produce a bad performance.

@valerybugakov What we currently do is reading the .babelrc ourselfs, transforming the paths and then supplying the config to babel-register. It is not really nice.

    var babelCfg = JSON.parse(fs.readFileSync(path.join(rootPath, '.babelrc'), 'utf8'));

    babelCfg.plugins.forEach((plugin, index) => {
        if (typeof plugin === 'string' || plugin[0] !== 'module-resolver') return;

        Object.keys(plugin[1].alias).forEach(alias => {
            plugin[1].alias[alias] = path.join(rootPath, plugin[1].alias[alias]);
        });
    });

    babelCfg.babelrc = false;

    require('babel-register')(babelCfg);

@tleunen Yes that should work I think, I'm looking into the pm2 options now maybe that helps.

@tleunen I fixed my problem by setting the correct cwd in pm2.

Just a heads up that if a user has multiple .babelrc throughout their project the file being transpiled will use the closest .babelrc file.

So you do really need to look up per file if you want to handle how people currently use babel.

https://babeljs.io/docs/usage/babelrc/#lookup-behavior

You're right @luke-john, that's what was done in #71 but I wonder the performance impact that this could have.
Because most projects only have 1 babelrc configuration anyway I guess...

If any of you are trying to get this working with Ava, I came up with a solution. This works on both my local machine and Travis.

  • Babel v6.5.2
  • Ava v0.16.0
  • babel-plugin-module-resolver v2.2.0

1) Setup your package.json like so:

"scripts": {
   "test": "BABEL_DISABLE_CACHE=1 PWD=$(pwd) NODE_ENV=test ava",
   "test:watch": "npm test -- --watch"
},

"ava": {
   "verbose": true,
   "babel": "inherit",
   "require": [
      "babel-register",
      "./test/helpers/pwd.js",
      "./test/helpers/setup-browser-env.js"
    ]
  },

2) Change the pwd before each test (I implemented in test/helpers/pwd.js):

/**
  Note: Set the PWD to the root of the project (see package.json where we set the ENV variable)
  We need to do this to get `babel-plugin-module-resolver` working with Ava. The issue
  is that Ava changes the PWD to each test's location.
*/
process.chdir(process.env.PWD)

Or, to avoid adding a load of hacks, use https://github.com/jshanson7/babel-plugin-resolver.
I'd like to switch to this module because it has an eslint-plugin-import resolver but this AVA bug is stopping me.

babel-plugin-resolver seems to use the same root as the one containing node_modules, which could be incorrect as well...

Could you describe the structure of your app @MethodGrab ? Where is the babel conf file compared of the root app?

@MethodGrab Did my solution not work for you?

@tleunen It's in the root. The structure looks something like this:

api
bin/api.js // main entry point that loads babel-register, babel-polyfill and requires ../api/index.js
src
lib
node_modules
test
.babelrc
package.json

babel-plugin-module-resolver works fine for the main application code, it's just the AVA tests that can't resolve modules correctly.


@timkendall I'm on Windows so I can't set inline environment variables unfortunately.

Gotcha, so #71 should resolve the issue.. I'll try to find some time this weekend to get this in

Hopefully! Thanks :+1:

Is it because ava sets a custom pwd when it runs? Would you have a small test project so I can take a look how it works?

I'm still having doubts on the proper way to manage all this. I wonder if the paths should be always relative to the root project, or based on babelrc/package.json file if a babel config is found there. But with babel-register, what should be set then? :s

Heres a minimal test case that should allow you to reproduce the issue: https://github.com/MethodGrab/babel-plugin-module-resolver-ava-test-case.

The problem seems to be with trying to import from inside files in the helpers dir.
This might be a separate issue from the one originally mentioned in this thread.

Thanks @MethodGrab, this helps a lot.
So indeed, from what I see, ava is changing the working directory for each test.

I'm updating my branch with the custom pwd based on each file, I'll release a beta version so people can test it in their project first and see if there're any big slowdown because of that.

I have just released 3.0.0-beta.1. I tested it in my own project and it seems to work fine. Released as beta for now because I want to make sure it resolves the issues for everyone, so please test and let me know it all's good :)

Also, with this 3.0.0-beta.1, you must also update eslint-import-resolver-babel-module to 3.0.0-beta.1, otherwise you'll get false eslint errors.

Unfortunately 3.0.0-beta.1 doesn't seem to have resolved the issue in my test case, it still has the same missing module error.
Note: I haven't committed 3.0.0-beta.1 so if you clone the repo, you'll need to update to 3.0.0-beta.1 manually (npm install babel-plugin-module-resolver@beta).

Are you sure?
I'm seeing this:

foo-helper.js :: process.cwd() /Users/tommy.leunen/.Trash/ava-test2/test/unit
========== begin foo.spec.js ==========
foo.spec.js :: process.cwd() /Users/tommy.leunen/.Trash/ava-test2/test/unit
foo.js :: process.cwd() /Users/tommy.leunen/.Trash/ava-test2/test/unit
  ✔ #foo should return 'foo'

Hmm, this is odd. On Windows (the machine I've been using to test so far) I'm still seeing the errors with 3.0.0-beta.1 (even after trashing my node_modules dir and reinstalling), but on my Mac both 2.2.0 and 3.0.0-beta.1 work fine!
This was the first time I ran the test case on my Mac so maybe its being cached somewhere other than node_modules 🤔.

I was testing on the mac and I can say I had the error with 2.2.0 but no errors with 3.0 beta.
I'll try on windows later today.

You can also set Babel to not cache anything BABEL_DISABLE_CACHE=1

Ah, with BABEL_DISABLE_CACHE=1 2.2.0 does error on my Mac. And BABEL_DISABLE_CACHE=1 also fixes the Windows build!
I assumed ava --no-cache would prevent all caching, I guess not.

Great work on the fix :+1: !

Did anyone find any issue with the beta version?

It's been working fine for me :+1:

Just to let you know I've just release a version 2.3.0. with the changes I initially made for 3.0 but I moved them behind a custom cwd option value.

So you can safely downgrade your 3.0.0-beta.1 version for the 2.3.0, benefits from the recent fixes and use the cwd option in your plugin configuration if you'd like. More on that here: https://github.com/tleunen/babel-plugin-module-resolver#options

Was this page helpful?
0 / 5 - 0 ratings

Related issues

lsiden picture lsiden  Â·  8Comments

lizhuoyuan picture lizhuoyuan  Â·  7Comments

kkomaz picture kkomaz  Â·  7Comments

WrathChaos picture WrathChaos  Â·  6Comments

tleunen picture tleunen  Â·  6Comments