tleunen / babel-plugin-module-resolver

Custom module resolver plugin for Babel
MIT License
3.46k stars 205 forks source link

Module extensions not working? #201

Open tristan-shelton opened 7 years ago

tristan-shelton commented 7 years ago

I'm trying to use this plugin to handle module resolution in my Jest unit tests for a Webpack project and it works pretty well, but I can't seem to get module extensions to work. My .babelrc looks like this:

{
  <...>
  "env": {
    "test": {
      "plugins": [
        ["module-resolver", {
          "root": ["./src", "./test"],
          "extensions": [".js", ".jsx", ".json"]
        }]
      ]
    }
  }
}

Then I created the file test/fixtures/foo.json and inside of test/class/Test_spec.js I do this:

import foo from 'fixtures/foo'

which throws this error:

    Cannot find module '../fixtures/foo' from 'Test_spec.js'
      at Resolver.resolveModule (node_modules/jest-resolve/build/index.js:179:17)
      at Object.<anonymous> (test/class/Test_spec.js:6:189)

If I add the extension:

import foo from 'fixtures/foo.json'

Everything works as expected. Any suggestions would be appreciated!

Relevant versions:

λ node -v
v6.10.1
λ npm list babel-core babel-plugin-module-resolver
+-- babel-core@6.25.0
`-- babel-plugin-module-resolver@2.7.1
tleunen commented 6 years ago

Hmm.. Afaik, the json extension is not mandatory in node either so your path '../fixtures/foo' should definitely work.

Would you have a test repo by any chance?

amosyuen commented 6 years ago

After poking through the code, there seems to be two problems in v3. In the examples cited I am using these options for module resolver:

{
    root: ['.'],
    extensions: ['.web.js', '.js', '.json', ''],
}

and I am trying to resolve a file at src/app/Link.web.js and babel is running in src/

1) The extension is removed from the resolved file path if it doesn't match the import extensions. In src/resolvePath.js:32

    const ext = realSourceFileExtension === sourceFileExtension ? realSourceFileExtension : '';
So if I do `import 'app/Link';` which resolves to `src/app/Link.web.js`, since `'' !== '.web.js'` then the extension will be removed and it will be transformed to `require('./Link')`.

If I remove your extension stripping logic, it works fine. I don't understand why you are stripping the extensions at all, shouldn't you just be using the file that was resolved?

2) You don't resolve extensions for relative paths In src/resolvePath.js:78

    const resolvers = [
      resolvePathFromAliasConfig,
      resolvePathFromRootConfig,
    ];
You only do resolutions if it's from a root or an alias. So this code `import './Link'` will be unchanged rather than being resolved to `require('./Link.web.js')` as I would expect it to.
amosyuen commented 6 years ago

I created PR https://github.com/tleunen/babel-plugin-module-resolver/pull/247 to address those two issues. I set stripExtensions by default to the extensions param to preserve the old behavior of stripping extensions specified.

tleunen commented 6 years ago

I don't understand why you are stripping the extensions at all, shouldn't you just be using the file that was resolved?

Because the extensions are not needed for node find the file. This logic was added for RN because of the custom .ios.js and .android.js, and afaik, they don't need to be explicit as well since they use a custom resolver, so they can also be removed.

But it seems that in your case, you'd like to keep .web.js otherwise node cannot find the file... Is that right?

@fatfisz This is funny, I have the feeling this is a good use case of a custom resolvePath function or the new function for aliases as well, since we could check for the platform and then resolve the file based on it.

amosyuen commented 6 years ago

Yep, that's correct that I need to keep the .web.js function.

Thanks for explaining, makes more sense now as why it works the way it does.

While it might be nice to resolve the extensions based on platform, I still think it would be good to give the user explicit control as there will always be other scenarios which we can't detect by platform. e.g. my use of .web.js with react-native-web`.

wardpeet commented 6 years ago

@tleunen I stumbled upon the same issue, I wrote my own plugin real quick but what's the reason for not allowing relative paths?

tleunen commented 6 years ago

@wardpeet There wasn't any real reason to be honest. My first implementation didn't use any custom extension (that was done later on in the development process), so it wasn't needed and was not done to make the plugin faster.

But now with the custom extension, it makes sense to read all paths, I agree. And I'd like to merge this PR which will resolve your issue as well #259

wardpeet commented 6 years ago

Awesome! Keep up the good work!

m860 commented 6 years ago

alias is working but extensions is not working for me! .babelrc "env": { "test": { "plugins": [ [ "module-resolver", { "extensions": [ ".browser.js", ".js", ".browser.json", ".json" ], "stripExtensions":[ ".server.js", ".server.json" ] } ] ] } }

` //logger.browser.js //logger.server.js import logger from "../src/libs/logger"

test("test", () => { logger.info("browser module") expect(1) }) `

Cannot find module '../src/libs/logger' from 'logger.test.js' at Resolver.resolveModule (node_modules/jest-resolve/build/index.js:169:17) at Object. (test/Loading.test.js:34:15)

wardpeet commented 6 years ago

@m860 you'll need a version that includes https://github.com/tleunen/babel-plugin-module-resolver/issues/234

m860 commented 6 years ago

@wardpeet node -v && npm -v && npm list babel-core babel-plugin-module-resolver v6.10.0 3.10.10 ├── babel-core@6.26.3 └── babel-plugin-module-resolver@3.1.1