import-js / eslint-plugin-import

ESLint plugin with rules that help validate proper imports.
MIT License
5.56k stars 1.57k forks source link

eslint-import-resolver-webpack don't work for resolved paths #352

Closed ideal-life-generator closed 8 years ago

ideal-life-generator commented 8 years ago

.eslintrc

{
  "parser": "babel-eslint",
  "extends": "airbnb",
  "plugins": ["import"],
  "settings": {
    "import/resolver": "webpack"
  },
  "env": {
    "browser": true
  }
}

webpack.config.js

...
  resolve: {
    root: path.resolve('src'),
    extensions: ['', '.js', '.scss'],
    modulesDirectories: ['node_modules'],
  },
...

src/index.js

import routes from 'routes';
import 'scss/index.scss';

in console:

Error - Unable to resolve path to module 'routes'. (import/no-unresolved)
Error - Unable to resolve path to module 'scss/index.scss'. (import/no-unresolved)
ljharb commented 7 years ago

@asdine a better solution is to name your actual webpack config webpack.config.babel.js and then webpack and eslint will do the babel transpilation for you.

sompylasar commented 7 years ago

@ljharb There are apps which do not use webpack.config.js file; the config is built with a function as a part of the build pipeline, in the middle of the build process. @asdine's solution helps to provide eslint-plugin-import's resolver with the webpack config file even if the way of obtaining a webpack config that is passed to the webpack compiler is different than just require('./webpack.config.js').

ljharb commented 7 years ago

@sompylasar i'm confused; if the webpack config file is ES6, it should be named ".babel" and everything's automatic. if it's not ES6, it shouldn't need babel-register.

sompylasar commented 7 years ago

@ljharb Everything's automatic when everything is default. When you have a custom build pipeline, there may be no webpack config file at all, neither webpack.config.babel.js nor webpack.config.js.

ljharb commented 7 years ago

I'm very confused what kind of custom build pipeline one would have that utilizes a webpack config, claims to not have a file at all, but then passes a file path to the eslint resolver config?

In other words, if there's no file, then there's nothing to require in https://github.com/benmosher/eslint-plugin-import/issues/352#issuecomment-291967058 - if there is a file, then you can name it .babel.js to avoid needing babel-register.

sompylasar commented 7 years ago

There is no other way to configure eslint resolver I know about than to pass the file path. If there is, please tell me. Anyway, for the text editor to use eslint, we have to have a eslintrc (luckily this can be in executable JavaScript, not just static JSON). That file path passed to eslint resolver config is not used by the build pipeline (except for linting), the webpack compiler is called as a function from the build pipeline, and a generated webpack config is passed there.

sompylasar commented 7 years ago

By the way, which module implements the magic .babel.js handling? That's obviously not Node itself, so if my eslintrc is called from Atom Linter it won't have the necessary environment for the magic to happen.

ljharb commented 7 years ago

That's webpack itself; I'd assume eslint would do it too.

anisimovyuriy commented 7 years ago

npm install --save-dev eslint-import-resolver-webpack - resolved the issue!

benmosher commented 7 years ago

By the way, which module implements the magic .babel.js handling?

@sompylasar: the Webpack resolver does it to match Webpack proper (which also does it). so it ought to work in any ESLint integration environment.

sompylasar commented 7 years ago

@benmosher ESLint does not rely on Webpack, so does not Atom Linter, so when Atom Linter calls ESLint, there is no Webpack magic built in.

benmosher commented 7 years ago

clarification: eslint-import-resolver-webpack implements the aforementioned magic.

So: Atom Linter => ESlint => eslint-plugin-import => eslint-import-resolver-webpack => ✨magic✨

iamgutz commented 7 years ago

resolved by adding the webpack resolver plugin to the eslintrc config and CHANGING in the webpack config files require path in ES5 style.

// webpack.config.dev.js
const path = require('path'); // instead of import path from 'path'

// .eslintrc
"settings": {
      "import/resolver": {
        "webpack": {
          "config": "webpack.config.dev.js"
        }
      }
    }

// package.json
"eslint-import-resolver-webpack": "^0.8.2",
kettanaito commented 7 years ago

I might as well be wrong, but it should be possible to use ES6 syntax with Webpack configuration if you rename it to webpack.babel.js. This should not throw any errors from configuration validation part of eslint's Webpack resolver.

iamgutz commented 7 years ago

@asdine your solution works! Thanks!

bramschulting commented 7 years ago

I'm running into this issue as well, but only when viewing files in Atom.

When I run node_modules/.bin/eslint src/js/app/modules/sectionsPage/containers/SectionTileContainer.js, everything works fine. But in Atom I get import/no-unresolved errors.

If I run DEBUG=eslint-plugin-import:* $(npm bin)/eslint src/js/app/modules/sectionsPage/containers/SectionTileContainer.js I can see the import/resolve fail, but I don't understand why. This is my output (I cut off a bit at the end):

eslint-plugin-import:resolver:node Resolving: +0ms stores/AuthStore from: /Users/bramschulting/projects/web-client/src/js/app/modules/sectionsPage/containers/SectionTileContainer.js
eslint-plugin-import:resolver:node resolve threw error: +4ms Error: Cannot find module 'stores/AuthStore' from '/Users/bramschulting/projects/web-client/src/js/app/modules/sectionsPage/containers'
  at Function.module.exports [as sync] (/Users/bramschulting/projects/web-client/node_modules/resolve/lib/sync.js:36:11)
  at Object.exports.resolve (/Users/bramschulting/projects/web-client/node_modules/eslint-import-resolver-node/index.js:19:28)
  at v2 (/Users/bramschulting/projects/web-client/node_modules/eslint-module-utils/resolve.js:79:23)
  at withResolver (/Users/bramschulting/projects/web-client/node_modules/eslint-module-utils/resolve.js:84:16)
  at fullResolve (/Users/bramschulting/projects/web-client/node_modules/eslint-module-utils/resolve.js:101:22)
  at relative (/Users/bramschulting/projects/web-client/node_modules/eslint-module-utils/resolve.js:46:10)
  at resolve (/Users/bramschulting/projects/web-client/node_modules/eslint-module-utils/resolve.js:172:12)
  at checkSourceValue (/Users/bramschulting/projects/web-client/node_modules/eslint-plugin-import/lib/rules/no-unresolved.js:29:50)
  at checkSourceValue (/Users/bramschulting/projects/web-client/node_modules/eslint-module-utils/moduleVisitor.js:29:5)
  at EventEmitter.checkSource (/Users/bramschulting/projects/web-client/node_modules/eslint-module-utils/moduleVisitor.js:34:5)
eslint-plugin-import:resolver:webpack Config path from settings: /Users/bramschulting/projects/web-client/webpack.config.js +0ms
eslint-plugin-import:resolver:webpack Config path resolved to: /Users/bramschulting/projects/web-client/webpack.config.js +1ms
eslint-plugin-import:resolver:webpack Using config:  { cache: true,
debug: true,
devtool: 'source-map',
resolve:
 { root: '/Users/bramschulting/projects/web-client',
   extensions: [ '', '.js', '.jade' ],
   modulesDirectories:
    [ 'node_modules',
      'src/js/app',
      'src/js/vendor_modules',
      'tests/mock' ],
   alias:
    { environment: '/Users/bramschulting/projects/web-client/src/js/app/config/env/local.js',
      byebye: '/Users/bramschulting/projects/web-client/src/js/app/libs/byebye/byebye.js',
      q: '/Users/bramschulting/projects/web-client/src/js/app/libs/q.js',
      templates: '/Users/bramschulting/projects/web-client/src/templates',
      backbone: 'exoskeleton/exoskeleton',
      'appboy-web-sdk': 'appboy-web-sdk/appboy.core.min.js',
      'react/lib/ReactMount': 'react-dom/lib/ReactMount' } },
...

The line which failes is:

import AuthStore from 'stores/AuthStore';

But that is located in /Users/bramschulting/projects/web-client/src/js/app/stores/AuthStore.js, so the webpack resolve config should be correct, right?

Does anyone have an idea what's going on? This issue is driving me nuts 🔩

sompylasar commented 7 years ago

@bramschulting If you look into the error stacktrace, it is coming from eslint-import-resolver-node, while you expect it to use eslint-import-resolver-webpack. Seems you have configured both, and node takes precedence over webpack.

bramschulting commented 7 years ago

@sompylasar Oh right, thanks! That's due to a config we're extending (airbnb). Is there a way to disable this? I can manually overwrite it, but I'd prefer to disable it (as it's redundant) or 'use' the webpack config for the node resolver. Is that possible?

sompylasar commented 7 years ago

@bramschulting I use it this way in my .eslintrc.js:

  "settings": {
    "import/ignore": [
      "node_modules",
      "\\.(scss|less|css)$",
    ],
    "import/resolver": {
      "node": {
        "moduleDirectory": [
          "./src/webapp",
          "./src/api",
          "./src/server",
          "./src/common",
        ],
      },
      "webpack": {
        "config": "./webpack/configForEslintImportResolver.js",
      },
    },
  },
bramschulting commented 7 years ago

@sompylasar yeah I was thinking of this too. But the downside of this is that you still have to define the moduleDirectory in two locations (in .eslintrc and your webpack config).

sompylasar commented 7 years ago

@bramschulting Right. Well, I didn't go extra mile here, but you can require that info from a separate JS file 1) from .eslintrc.js to give the module roots to eslint-import-resolver-node and 2) from the webpack config for the eslint-import-resolver-webpack to give the module roots to it.

bramschulting commented 7 years ago

@sompylasar OK, I'm running into another issue 🙈 I'm getting errors when importing files that have an alias in my webpack config, because the node-resolver tries to resolve them first I think.

I've been looking at this module to figure out where the order is being determined, but I can't seem to find it. Could you give me some pointers? I'd like to make a PR which allows for setting the resolve order, maybe like this:

    "import/resolver": {
      "order": ["webpack", "node"],
      "webpack": {
        "config": "webpack/config.js"
      }
    }

That would fix all the problems I mentioned, right?

sompylasar commented 7 years ago

@bramschulting The order of iterating over the configured resolvers is defined by the Map iteration order: https://github.com/benmosher/eslint-plugin-import/blob/dc3609f895f5ad390d88fd9925dce40960117930/utils/resolve.js#L92-L97 But from the code, the import/resolver config could be an array: https://github.com/benmosher/eslint-plugin-import/blob/dc3609f895f5ad390d88fd9925dce40960117930/utils/resolve.js#L117-L120

I assume it could look like this, but I haven't tested it:

    "import/resolver": [
      {
        "webpack": {
          "config": "./webpack/config.js"
        }
      },
      {
        "node": {
          "moduleDirectory": [
            "whatever"
          ]
        }
      }
    ]

I also recommend to write relative paths explicitly with ./, to make them more visible (also someone reported that the path without ./ didn't work for them, but I doubt this is true).

sompylasar commented 7 years ago

The array shape of import/resolver is not documented, by the way, but that is a documentation issue: https://github.com/benmosher/eslint-plugin-import/blob/ccd93942a0b9f59f1b698672ba167ece1aa9f5f1/README.md#resolvers CC @benmosher

gricard commented 6 years ago

Just wanted to thank @sompylasar for his example of ignoring node_modules. That resolved an issue that broken eslint for us. Thank you!!

sompylasar commented 6 years ago

@gricard You're welcome!

aeolusheath commented 5 years ago

as @arkaitzgarro said, it works for me.

but I didn't use path.

config: path.join(__dirname, 'webpack.config.js')

my .eslintrc.js :

module.exports = {
 //... other configs
  plugins: [
    'vue',
    'import'
  ],
  settings: {
    "import/resolver": {
        "webpack": {
            "config": "./build/webpack.base.conf.js"
        }
    }
  },
}

files location:

|-- package.json
|-- .eslintrc.js
|-- build
    |-- webpack.base.conf.js
LittleBreak commented 5 years ago

MARK

qbalin commented 4 years ago

What did it for me was to move extensions: ['*', '.js', '.jsx'] from module.rules[0].resolve to the root resolve field (in webpack.config.js). .jsx files were problematic.

Before:

  resolve: {
    alias: {
      component: path.resolve(__dirname, 'frontend/component/'),
      store: path.resolve(__dirname, 'frontend/store/'),
      lib: path.resolve(__dirname, 'frontend/lib/'),
      context: path.resolve(__dirname, 'frontend/context/'),
    },
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /(node_modules)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env', '@babel/preset-react'],
            plugins: ['@babel/plugin-proposal-object-rest-spread'],
          },
        },
        resolve: {
          extensions: ['*', '.js', '.jsx'], // <-- This has to be moved up
        },
      },
      {
        test: /\.css$/i,
        use: ['style-loader', 'css-loader'],
      },
    ],
  },

After:

  resolve: {
    alias: {
      component: path.resolve(__dirname, 'frontend/component/'),
      store: path.resolve(__dirname, 'frontend/store/'),
      lib: path.resolve(__dirname, 'frontend/lib/'),
      context: path.resolve(__dirname, 'frontend/context/'),
    },
    extensions: ['*', '.js', '.jsx'], // <-- Here, it works 
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /(node_modules)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env', '@babel/preset-react'],
            plugins: ['@babel/plugin-proposal-object-rest-spread'],
          },
        },
      },
      {
        test: /\.css$/i,
        use: ['style-loader', 'css-loader'],
      },
    ],
  },
moonjoungyoung commented 3 years ago

@arkaitzgarro

Thank you so much. You solved my one-year problem.