tleunen / eslint-import-resolver-babel-module

Custom eslint resolve for babel-plugin-module-resolver
https://github.com/tleunen/babel-plugin-module-resolver
MIT License
248 stars 31 forks source link

Support babel.config.js #89

Open ohtangza opened 6 years ago

ohtangza commented 6 years ago

As of Babel 7, it's recommended to use babel.config.js. Any plan to support it?

tleunen commented 5 years ago

Thanks for asking. I'll add it when time allows it :)

ohtangza commented 5 years ago

@tleunen Thanks for your response! I'll look forward to it. Please let me know when you need any reviewer.

IanVS commented 5 years ago

Hi, I'm attempting to migrate over to babel 7, and am using a babel.config.js file outside of my project root. I see that there's a 5.0 beta that supports babel 7, but does not find my config file yet. Is my best bet to specify my babel-module config inside of my eslint config file?

mogelbrod commented 5 years ago

I'm having the same issue after migrating to babel.config.js (changing nothing else), even with npm i eslint-import-resolver-babel-module@5.0.1 babel-plugin-module-resolver@3.2.0

.eslintrc (unchanged)

{
  "parser": "babel-eslint",
  "extends": [
    "eslint:recommended",
    "plugin:import/errors",
    "plugin:import/warnings"
  ],
  "settings": {
    "import/resolver": {
      "babel-module": {}
    }
}

babel.config.js (converted from .babelrc)

module.exports = {
  presets: [
    '@babel/preset-env',
  ],
  plugins: [
    [
      'module-resolver',
      {
        root: [
          './app'
        ],
        transformFunctions: [
          'require',
          'require.context',
          'require.resolve',
          'import'
        ]
      }
    ],
  ],
  // ...
}
mogelbrod commented 5 years ago

Update: Running eslint src from the same directory as babel.config.js actually works, but the following doesn't:

cd src
eslint file-to-lint.js
../src/file-to-lint.js
  12:19  error  Unable to resolve path to module '...'      import/no-unresolved

I got the above to work as well by copying the plugins[].module-resolver babel.config.js section into .eslintrcs settings.import/resolver.babel-module. Looks like eslint-import-resolver-babel-module can't find babel.config.js if it's located in parent directory?

tilgovi commented 5 years ago

I think what is needed is for the getPlugins call to pass rootMode as an option to the option manager initialization. Probably rootMode: 'upward-optional'.

101 properly sets the current working directory for the initialization of Babel's option manager so that it performs discovery from the correct place. However, with babel.config.js in a monorepo setup the current working directory may have a package.json file as a closer ancestor than babel.config.js. If that happens, Babel stops looking upwards for a configuration file.

Since .babelrc cannot contain rootMode or root options, there is no workaround that can be achieved through configuration.

AylwSt01 commented 4 years ago

Any update on this? I need this too :(

tilgovi commented 4 years ago

As far as I can tell from reading code, it seems as though this should already work if you specify a root option in the import/resolver babel-module settings to the directory containing the babel.config.js.

Doing anything like I said in my previous comment is probably unsafe, because it could cause a babel.config.js outside the project directory to be used.

tilgovi commented 4 years ago

I am mistaken. I had dug into this before and come to an earlier conclusion. I returned to try to make a PR and reached a different conclusion.

I'm searching for a solution that doesn't involve rootMode, for the reasons I mention above, but I haven't found it yet.

I think a solution may be to treat the ESLint root as the project root rather than the root found by pkg-up.

tilgovi commented 4 years ago

I've been able to solve this with a local workaround in my own project.

Here are the relevant parts of my .eslintrc.js:

const babel = require('@babel/core');

const babelOptions = babel.loadOptions({ cwd: __dirname });
const babelModuleResolver = babelOptions.plugins.find(
  ({ key }) => key === 'module-resolver',
);

// ...
    'import/resolver': {
      'babel-module': babelModuleResolver.options,
    }

// ...

With this setup, eslint-import-resolver-babel-module fails to resolve my babel configuration, so it uses the "default options" that are passed to it, and I manually ensure that these options are taken from the babel.config.js file. Change the cwd option to loadOptions if your .eslintrc.js is not in the same directory as babel.config.js and you need to customize the search path.

tleunen commented 4 years ago

Any idea why it falls to resolve in your project? Cause it would be great to fix this once and for all 🙂

tilgovi commented 4 years ago

Any idea why it falls to resolve in your project?

I think the root cause is the cwd passed to OptionsManager.prototype.init in getPlugins. The cwd option passed is taken from the result of a pkg-up search from the file being linted. This restricts Babel's search for configuration files to within that directory.

In my project, the nearest package.json file is deeper than the babel.config.js because I keep one babel.config.js at the root of a monorepo that holds several projects.

Babel will, by default, check a given root directory for babel.config.js file, but the default value for the root option is cwd.

The cwd option may not be necessary at all. One solution would be to remove it. That would cause babel to use process.cwd() as the working directory, so running ESLint from the root of the project would just work.

However, some users or IDEs may run ESLint from somewhere other than the root of the project. For example, my understanding is that this is why options like babelrc and packagejson were added to find-babel-config. For these users, it would be nice to specify the root directory.

Unfortunately, the root option already has a different meaning for babel-plugin-module-resolver, so it's difficult to use that. However, Babel does take a configFile option that we could allow users to specify. That would be unambiguous.

Taking all this into consideration, these are some options to consider:

  1. Remove the cwd option to OptionsManager.prototype.init because the result of a pkg-up search is not necessarily a better cwd for Babel than the actual current working directory.

  2. Instead of removing the cwd option, be consistent with babel-plugin-module-resolver options, allowing babelrc to be specified. For that, we may want to export normalizeOptions from babel-plugin-module-resolver.

  3. Allow a configFile option (in eslint-import-resolver-babel-module only, not babel-plugin-module-resolver) that is passed to OptionsManager.prototype.init so that the user can specify the Babel configuration file explicitly.

  4. We could cleanly separate the options passed to Babel from the options passed to babel-plugin-module-resolver. We could either introduce a babelOptions setting and pass it through to OptionsManager.prototype.init. A better choice might be to just treat all options of eslint-import-resolver-babel-module as Babel options and only get babel-plugin-module-resolver options from the Babel configuration.

It would be a breaking change, but I like this last option the best. I believe a Babel configuration file is the best place to configure babel-plugin-module-resolver and the ESLint settings for import-resolver-babel-module should only deal with finding the configuration and specifying the programmatic Babel options.

On a related issue, I'm not sure what the use cases are for the cwd option to babel-plugin-module-resolver at all. It seems like alias, root, and resolvePath should be enough to manage resolution. The current working directory could be pulled out of the Babel options during in manipulateOptions hook or relative paths could be treated as always relative to the file being compiled.

tleunen commented 3 years ago

However, some users or IDEs may run ESLint from somewhere other than the root of the project. For example, my understanding is that this is why options like babelrc and packagejson were added to find-babel-config. For these users, it would be nice to specify the root directory.

I would lie saying that I remember, but I believe you're right indeed. Running eslint in an IDE usually runs it from the file you run the linting process.

Ideally, I would like having this eslint plugin to be as simple as possible, without having a need to provide any options since most of it could be taken from the options of the babel plugin I believe. I'll be honest and it's been a while (years!) since I worked in this project or the babel plugin so there are many things I don't remember. I'd even say that if someone is interested in taking the lead, I'd be definitely interested in talking with them. Even if it means rewriting the babel plugin and eslint plugin to make them simpler and better. Currently it seems there's quite some confusion

tilgovi commented 3 years ago

I had some good success reconfiguring a project to use only babelOptions to supply rootMode upwards and everything else configured in babel.config.js. Configuring just root to be __dirname from eslint config would also probably work.

If you want help, I could lend a hand with packing the changes and we could try a major version that removes everything and makes all the options just babel options.

tleunen commented 3 years ago

Yes please @tilgovi :) Thank you so much for investigating and finding a solution