Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

no-extraneous-dependencies: aliases are not considered "project"-level imports #496

Open
dizzyn opened this issue Aug 15, 2016 · 58 comments

Comments

@dizzyn
Copy link

dizzyn commented Aug 15, 2016

Hi, not sure if it is a bug. If no sorry.

I use a Webpack resolve condition:

resolve: {
    alias: {
        "pg-face": __dirname + '/src/pg-face/src/'
    }
},

My includes look like:
import TableLine from 'pg-face/components/tables/TableLine'

And because of that I have the eslint report:

error 'pg-face' should be listed in the project's dependencies. Run 'npm i -S pg-face' to add it import/no-extraneous-dependencies

Have a nice day.

@benmosher
Copy link
Member

Ah, yeah, not so much a bug as a missing feature. 😅

It's tough to disambiguate "project" dependencies from node_modules dependencies ATM, so aliases may not play nicely with no-extraneous-dependencies.

@benmosher benmosher changed the title Webpack Resolve Alias no-extraneous-dependencies: aliases are not considered "project"-level imports Aug 15, 2016
@simonbuchan
Copy link

Is this the same issue with externals? Seems odd that import/resolve: webpack will quiet no-unresolved but not no-extraneous-dependencies.

@benmosher
Copy link
Member

benmosher commented Aug 17, 2016

@simonbuchan I suspect that's true. externals has a similar property to aliases in that it results in more path-resolution magic than no-extraneous-dependencies is prepared to handle.

Though I wouldn't be surprised if certain (if not all) externals definitions look like core modules instead of dependencies, but I'm not sure.

@benmosher
Copy link
Member

Upon further reflection, this is another case of #479, should be solvable the same way.

@BrandonBoone
Copy link

eslint-plugin-import supports custom resolvers (full list). Using eslint-import-resolver-webpack solved this problem for me.

@brettstack
Copy link

I'm using eslint-import-resolver-webpack, and I'm still getting this error even though I have it defined in my module.resolve.root.

@MauroJr
Copy link

MauroJr commented Dec 8, 2016

Even using eslint-import-resolver-webpack, it still not recognize webpack aliases and throw import/no-extraneous-dependencies

@huangyingjie
Copy link

In my case, you should not use special prefix char like @assets but assets directly, or this warning will throw.

@BrandonBoone
Copy link

Another thing to keep in mind when using eslint-import-resolver-webpack:

Will look for webpack.config.js as a sibling of the first ancestral package.json, or a config parameter may be provided with another filename/path either relative to the package.json, or a complete, absolute path.

If multiple webpack configurations are found the first configuration containing a resolve section will be used. Optionally, the config-index (zero-based) setting can be used to select a specific configuration.

I discovered this recently when changing my webpack.config.js to export multiple (non-standard) configurations:

// specialized configurations for my custom setup
module.exports = {
  config1: {},
  config2: {},
};

instead of the webpack standard of :

// array of configurations
module.exports = [{}, {}];

// single configuration
module.exports = {};

which caused this error to reappear.

@revett
Copy link

revett commented Jan 9, 2017

+1 still seeing issue

@lanlin
Copy link

lanlin commented Feb 21, 2017

not working with webpack's externals options.

webpack 2.2

@mudrz
Copy link

mudrz commented Feb 26, 2017

Not working for me either, webpack 2 (webpack correctly resolves the dependency)

folder structure

root
  package.json
  webpack.config.base.js
  eslintrc.js
  someFolder
    foo

package.json

    "eslint": "^3.16.1",
    "eslint-config-airbnb": "^14.1.0",
    "eslint-import-resolver-webpack": "^0.8.1",
    "eslint-plugin-import": "^2.2.0",
    "eslint-plugin-jsx-a11y": "^3.0.2 || ^4.0.0",

webpack.config.base.js

module.exports = {
  stats: { modules: false },
  resolve: {
    extensions: ['.js', '.jsx'],
    alias: {
      'foo': path.resolve(__dirname, `/someFolder/foo`),
    },
  },
}

eslintrc.js

module.exports={
    extends: ['airbnb'],
    parser: 'babel-eslint',
    settings: {
        'import/resolver': {
            'webpack': {
                'config': 'webpack.config.base.js'
            }
        }
    },
}

usage

import Something from 'foo'

ESLint Errors:

import/no-extraneous-dependencies
import/no-unresolved
import/extensions

@ljharb
Copy link
Member

ljharb commented Feb 28, 2017

Everywhere on Github, "+1" comments are not helpful nor necessary. Please indicate your support by adding a thumbs up reaction to the original post.

I will now delete the existing +1 comments, and any future ones. Thanks!

@machnicki
Copy link

eslint-import-resolver-webpack has worked pretty good with aliases in webpack 1.
After upgrade to webpack 2 problem happens again. Seems that webpack 2 is not supported yet.

@mudrz
Copy link

mudrz commented Mar 5, 2017

Just fyi it might be an issue with webpack after all:
webpack/webpack#4160

@kakadiadarpan
Copy link

kakadiadarpan commented Apr 28, 2017

The following configuration works for me.

Folder Structure:

root
  package.json
  .eslintrc.json
  config
    webpack.config.base.js
  src
    components
      component1.jsx
    containers
      container1.jsx

package.json

    "eslint": "^3.19.0",
    "eslint-config-airbnb": "^14.1.0",
    "eslint-import-resolver-webpack": "^0.8.1",
    "eslint-loader": "^1.7.1",
    "eslint-plugin-babel": "^4.1.1",
    "eslint-plugin-import": "^2.2.0",
    "eslint-plugin-jsx-a11y": "^4.0.0",

webpack.config.base.js

const path = require("path");
const paths = require("./paths");
// paths.appSrc points to src folder

module.exports = {
  context: paths.appSrc,
  entry: {
    vendor: [
      "react",
      "react-dom",
    ],
  },
  resolve: {
    extensions: [".js", ".jsx"],
    modules: [
      paths.appSrc,
      "node_modules"
    ],
    alias: {
      Components: path.resolve(paths.appSrc, "components/"),
    }
  }
};

.eslintrc.json

{
    "extends": "airbnb",
    "parser": "babel-eslint",
    "plugins": [
        "react",
        "jsx-a11y",
        "import"
    ],
    "settings": {
        "import/resolver": {
            "webpack": {
                "config": "config/webpack.config.base.js"
            }
        }
    }
}

container1.jsx

import component1 from "Components/component1";

@lemes
Copy link

lemes commented May 23, 2017

I was able to get rid of the ESLint errors by switching to babel-plugin-module-resolver. It's a good solution for project level imports until this issue get fixed.

.eslintrc

{
  "parser": "babel-eslint",
  "env": {
    "browser": true,
    "node": false,
    "jest": true
  },
  "extends": "airbnb",
  "settings": {
    "import/resolver": {
      "babel-module": {}
    }
  }
}

.babelrc

{
  "plugins" : [
    "transform-runtime",
    ["module-resolver", {
      "root": ["./src"]
    }]
  ],
  "presets" : [["es2015", { "modules": false }], "react", "stage-0"]
}

@chrisnicola
Copy link

Using the webpack resolver and seeing this depending on configuration. An alias like @ works but an alias like @name doesn't. I'm just going to disable this rule for now as it is unreliable when working with Webpack.

@ljharb
Copy link
Member

ljharb commented Jun 29, 2017

You're using an alias that's otherwise a valid npm scope? o.O

@machnicki
Copy link

machnicki commented Jun 29, 2017

eslint-import-resolver-webpack works very good with webpack 3: https://www.npmjs.com/package/eslint-import-resolver-webpack

I have need to add

  "settings": {
    "import/resolver": {
      "webpack": {}
    }
  },

in my .eslintrc

@chrisnicola
Copy link

@ljharb is that a constructive comment?

My point is simply that webpack supports this, while the import/resolver doesn't.

It's honestly fine with me if that's how it's going to work, but I'm assuming people will probably expect an alias that works with webpack to also just work with the webpack import/resolver.

@ljharb
Copy link
Member

ljharb commented Jun 30, 2017

I am asking because I'm genuinely surprised that anyone would ever attempt to make an alias like that. If you try a name that is not a valid npm name, does it work in both?

If so, that might be the way to fix this bug here.

@benmosher
Copy link
Member

#496 (comment)
@mudrz is your file on the filesystem foo or foo.js? if it's the latter, I'm surprised it doesn't work. if it's the former, I would believe that the resolver doesn't respect webpack 2's implicit no-extension resolution.

@mudrz
Copy link

mudrz commented Jul 8, 2017

@benmosher it was foo.js, imported as foo
There's something in my setup that I haven't figured out yet. Right now webpack aliases work (both in webpack builds and eslint import plugin), but only if I use capital letter for the alias name. They are not recognized if they start with a special symbol like @ or lowercase letter.
Unfortunately I made lots of other random changes (out of desperation) including switching from airbnb to the default react-create-app eslint configuration (+prettier), so it could be that the dependencies are different versions or that the issue has already been resolved.

I could test something if it would be of help to you, please let me know

@byronmejia
Copy link

Any updates from anyone yet? Currently trying to setup linters at work.

In webpack.config:

  extensions: ['', '.js', '.jsx', '.json'],
  resolve: {
    alias: {
      Api: path.resolve(__dirname, 'src/api'),
      Modules: path.resolve(__dirname, 'src/modules'),
// ...

In .eslintrc:

  "settings": {
    "import/resolver": {
      "webpack": {
        "config": "webpack.config.babel.js"
      }
    }
  },

In some file.jsx which fails:

import SubApp from 'Modules/subapp/container';
// This is a jsx file that exists

But this works:

import SubApp from 'Modules/subapp/container.jsx';
// This is a jsx file that exists

@mieszko4
Copy link

mieszko4 commented Jun 8, 2018

This change works for me #1119
But since I do not know any internals of the plugin I am sure there is a better way to rewrite that.

@ncaq
Copy link

ncaq commented Jul 11, 2019

I webpack upgrade. I see to fix.

@christineurban
Copy link

christineurban commented Sep 25, 2019

I tried literally everything in the post that led me here (microsoft/vscode-eslint#464) and this post. In the first post it said to try adding @babel/register to .eslintrc, so I converted my .eslintrc.json to .eslintrc.js. Then one by one I tried the rest. After all failed, I reverted .eslintrc.js to .eslintrc.json and thought, maybe I should try adding settings now, and this combination worked for me 🎉

I already had eslint-plugin-import and babel-plugin-module-resolver installed, so I just installed npm install --save-dev eslint-import-resolver-babel-module

.eslintrc.json

// ...

"settings": {
    "import/resolver": {
      "babel-module": {}
    }
  },

// ...

.babelrc.js

const path = require('path');
const APP_DIR = path.resolve(__dirname, 'admin/js/application');
const root = [path.resolve('./')];

const commonPlugins = [
  [
    require.resolve('babel-plugin-module-resolver'),
    {
      root,
      alias: {
          'app': APP_DIR,
          'marionette': 'backbone.marionette',
          'fileupload': 'blueimp-file-upload',
          'Fuse': 'fuse.js',
          'jquery-validate': 'jquery-validation',
          'bootstrap-confirmation': 'bootstrap-confirmation2/dist/bootstrap-confirmation.js',
          'test-helpers': path.resolve(__dirname, 'test-helpers'),
          'libs': path.resolve(__dirname, 'admin/js/libs'),
      }
    },
  ]
];

module.exports = {
  presets: [
      ['@babel/preset-env', {'modules': false} ]
  ],
  plugins: [
      '@babel/plugin-syntax-dynamic-import',
      '@babel/plugin-proposal-object-rest-spread',
      '@babel/plugin-proposal-class-properties',
      ...commonPlugins
  ],
  env: {
    'test': {
      'presets': [
        ['env', { 'targets': { 'node': 'current' }}]
      ]
    }
  }
};

@Shyam-Chen
Copy link

Shyam-Chen commented Dec 20, 2019

Replace eslint-import-resolver-webpack with eslint-import-resolver-alias

$ yarn add eslint-import-resolver-alias -D

My .eslintrc file:

Change

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

to

  "settings": {
    "import/resolver": {
      "alias": {
        "map": [
          ["~", "./src"]
        ],
        "extensions": [".mjs", ".js", ".vue"]
      }
    }
  },

And here is my webpack.config.js:

  resolve: {
    extensions: ['.mjs', '.js', '.vue'],
    alias: {
      vue$: 'vue/dist/vue.esm.js',
      '~': path.join(__dirname, 'src'),
    },
  },

@HughxDev
Copy link

HughxDev commented Sep 1, 2022

@Shyam-Chen’s solution initially worked for me, but the issue presents again when upgrading to version 2.25.4 or higher. eslint-import-resolver-alias has not been updated for 4 years, so it is possible that a recent change in eslint-plugin-import broke compatibility between the two. My solution for now is to just use 2.25.3.

@ljharb
Copy link
Member

ljharb commented Sep 1, 2022

@HughxDev i'd love to get a reproducible test case so i can fix that, if so.

@cha0s
Copy link

cha0s commented Feb 14, 2024

Just wanted to note here, I spent a lot of time debugging a similar issue and the problem was the order of import/resolver.

Make sure you run your webpack resolver before your node resolver to avoid this issue!

Doing this then started throwing e.g.

  error  'util' should be listed in the project's dependencies. Run 'npm i -S util' to add it  import/no-extraneous-dependencies

  error  'buffer' should be listed in the project's dependencies. Run 'npm i -S buffer' to add it  import/no-extraneous-dependencies

for all my node stuff ...

@cha0s
Copy link

cha0s commented Feb 14, 2024

Actually, that just broke a bunch of other stuff. Yikes!

@ljharb
Copy link
Member

ljharb commented Feb 14, 2024

That's still the correct thing to do, though - the node resolver should basically always be last.

@cha0s
Copy link

cha0s commented Feb 15, 2024

No, it's not correct.

@ljharb
Copy link
Member

ljharb commented Feb 15, 2024

@cha0s can you explain why you think it's not correct?

@cha0s
Copy link

cha0s commented Feb 15, 2024

Sure! (one of) My webpack target(s) is node. Therefore, I need webpack resolution as well as node resolution. This utility seems to only offer an either-or approach despite appearing to support multiple resolvers.

@ljharb
Copy link
Member

ljharb commented Feb 15, 2024

right - so your resolvers object should have two keys: webpack, and then node.

If that ignores the node resolver it'd only be if the webpack resolver is resolving the path first.

@cha0s
Copy link

cha0s commented Feb 15, 2024

Yup. The rest of the config is filled out dynamically.

This is what the final config looks like:

{
  "extends": [
    "/absolute/path/to/flecks/node_modules/eslint-config-airbnb/index.js",
    "/absolute/path/to/flecks/node_modules/eslint-config-airbnb/hooks.js"
  ],
  "globals": {
    // Not going to paste this ...
  },
  "ignorePatterns": [
    "dist/**",
    "build/flecks.hooks.js",
    "test/lint/fail.js"
  ],
  "overrides": [
    {
      "files": [
        "build/**/*.js"
      ],
      "rules": {
        "import/no-extraneous-dependencies": [
          "error",
          {
            "devDependencies": true
          }
        ],
        "import/no-dynamic-require": "off",
        "global-require": "off"
      }
    },
    {
      "files": [
        "test/**/*.js"
      ],
      "rules": {
        "prefer-arrow-callback": [
          "error",
          {
            "allowNamedFunctions": true
          }
        ],
        "brace-style": "off",
        "class-methods-use-this": "off",
        "import/no-extraneous-dependencies": "off",
        "import/no-unresolved": "off",
        "max-classes-per-file": "off",
        "no-new": "off",
        "no-unused-expressions": "off",
        "padded-blocks": "off"
      }
    }
  ],
  "parser": "/absolute/path/to/flecks/node_modules/@babel/eslint-parser/lib/index.cjs",
  "parserOptions": {
    "requireConfigFile": false,
    "babelOptions": {
      "configFile": "/absolute/path/to/flecks/node_modules/@flecks/build/build/babel.config.js"
    }
  },
  "plugins": [
    "@babel"
  ],
  "rules": {
    "brace-style": [
      "error",
      "stroustrup"
    ],
    "import/no-import-module-exports": "off",
    "import/prefer-default-export": "off",
    "jsx-a11y/control-has-associated-label": [
      "error",
      {
        "assert": "either"
      }
    ],
    "jsx-a11y/label-has-associated-control": [
      "error",
      {
        "assert": "either"
      }
    ],
    "no-param-reassign": [
      "error",
      {
        "props": false
      }
    ],
    "no-plusplus": "off",
    "no-shadow": "off",
    "object-curly-spacing": "off",
    "padded-blocks": [
      "error",
      {
        "classes": "always"
      }
    ],
    "yoda": "off"
  },
  "settings": {
    "import/resolver": {
      "webpack": {
        "config": {
          "resolve": {
            "alias": {
              "@flecks/db": "/absolute/path/to/flecks/packages/db/src",
              "@flecks/web": "/absolute/path/to/flecks/packages/web/src",
              "@flecks/socket": "/absolute/path/to/flecks/packages/socket/src",
              "@flecks/session": "/absolute/path/to/flecks/packages/session/src",
              "@flecks/server": "/absolute/path/to/flecks/packages/server/src",
              "@flecks/repl": "/absolute/path/to/flecks/packages/repl/src",
              "@flecks/redux": "/absolute/path/to/flecks/packages/redux/src",
              "@flecks/redis": "/absolute/path/to/flecks/packages/redis/src",
              "@flecks/react-redux": "/absolute/path/to/flecks/packages/react-redux/src",
              "@flecks/react": "/absolute/path/to/flecks/packages/react/src",
              "@flecks/passport-react": "/absolute/path/to/flecks/packages/passport-react/src",
              "@flecks/passport-local-react": "/absolute/path/to/flecks/packages/passport-local-react/src",
              "@flecks/passport-local": "/absolute/path/to/flecks/packages/passport-local/src",
              "@flecks/passport": "/absolute/path/to/flecks/packages/passport/src",
              "@flecks/headless": "/absolute/path/to/flecks/packages/headless/src",
              "@flecks/fleck": "/absolute/path/to/flecks/packages/fleck/src",
              "@flecks/electron": "/absolute/path/to/flecks/packages/electron/src",
              "@flecks/dox": "/absolute/path/to/flecks/packages/dox/src",
              "@flecks/docker": "/absolute/path/to/flecks/packages/docker/src",
              "@flecks/create-fleck": "/absolute/path/to/flecks/packages/create-fleck/src",
              "@flecks/create-app": "/absolute/path/to/flecks/packages/create-app/src",
              "@flecks/core": "/absolute/path/to/flecks/packages/core/src",
              "@flecks/build": "/absolute/path/to/flecks/packages/build/src",
              "/absolute/path/to/flecks/packages/db/node_modules/webpack": "/absolute/path/to/flecks/packages/db/node_modules/webpack"
            },
            "extensions": [
              ".mjs",
              ".js",
              ".json",
              ".wasm"
            ],
            "fallback": {
              "@flecks/db": "/absolute/path/to/flecks/packages/db",
              "@flecks/web": "/absolute/path/to/flecks/packages/web",
              "@flecks/socket": "/absolute/path/to/flecks/packages/socket",
              "@flecks/session": "/absolute/path/to/flecks/packages/session",
              "@flecks/server": "/absolute/path/to/flecks/packages/server",
              "@flecks/repl": "/absolute/path/to/flecks/packages/repl",
              "@flecks/redux": "/absolute/path/to/flecks/packages/redux",
              "@flecks/redis": "/absolute/path/to/flecks/packages/redis",
              "@flecks/react-redux": "/absolute/path/to/flecks/packages/react-redux",
              "@flecks/react": "/absolute/path/to/flecks/packages/react",
              "@flecks/passport-react": "/absolute/path/to/flecks/packages/passport-react",
              "@flecks/passport-local-react": "/absolute/path/to/flecks/packages/passport-local-react",
              "@flecks/passport-local": "/absolute/path/to/flecks/packages/passport-local",
              "@flecks/passport": "/absolute/path/to/flecks/packages/passport",
              "@flecks/headless": "/absolute/path/to/flecks/packages/headless",
              "@flecks/fleck": "/absolute/path/to/flecks/packages/fleck",
              "@flecks/electron": "/absolute/path/to/flecks/packages/electron",
              "@flecks/dox": "/absolute/path/to/flecks/packages/dox",
              "@flecks/docker": "/absolute/path/to/flecks/packages/docker",
              "@flecks/create-fleck": "/absolute/path/to/flecks/packages/create-fleck",
              "@flecks/create-app": "/absolute/path/to/flecks/packages/create-app",
              "@flecks/core": "/absolute/path/to/flecks/packages/core",
              "@flecks/build": "/absolute/path/to/flecks/packages/build"
            },
            "modules": [
              "node_modules"
            ],
            "symlinks": false
          }
        }
      },
      "node": {}
    },
    "react": {
      "version": "18.2.0"
    }
  }
}

Fails on the socket package if line linked is removed.

If you're interested in reproducing locally, I'll write out the few commands you have to run against that project to do so.

@ljharb
Copy link
Member

ljharb commented Feb 15, 2024

That package doesn't define a "main" or an "exports" (not that we support "exports" yet), and it doesn't have an index.js https://github.com/cha0s/flecks/tree/master/packages/socket

I do see that the build process is supposed to produce an index.js - is that file present when the failure occurs?

@cha0s
Copy link

cha0s commented Feb 16, 2024

It has a src/index.js. See: https://cha0s.github.io/flecks/docs/building-your-fleck#packagejson-and-entry-points

I don't understand your question. Are you asking me if lint is run against the build artifact? The answer to that question would be no. Lint is run against the source code.

May I ask why that's relevant? It may be useful to others to know if lint will break if they structure their files a certain way.

@ljharb
Copy link
Member

ljharb commented Feb 16, 2024

Right - so, without a build artifact, that package doesn’t have a “main” and is thus unresolvable.

Obviously you wouldn’t lint built code - but unlike most eslint plugins, this one runs across files. So when linting a file that imports that package, that package’s build output must exist or else you’ll get failures.

@cha0s
Copy link

cha0s commented Feb 16, 2024

I found that removing the node resolver entirely does not throw an error at all when linting e.g. https://github.com/cha0s/flecks/blob/master/packages/web/src/server/http.js, a full-blown node consumer using fs, http, path, and stream. It seems that the node resolver actually does nothing when put after the webpack resolver!

So when linting a file that imports that package, that package’s build output must exist or else you’ll get failures.

That's an interesting aside, but the error is not a result of importing the socket package. I linked the source of the error, it's the util import.

The examples I've provided are open source: CI and all. If you're interested in reproducing the bug locally to develop a fix, I will help if you need assistance building the project.

@ljharb
Copy link
Member

ljharb commented Feb 16, 2024

Interesting. I'd prefer a much smaller repro, tbh.

Fails on the socket package if line linked is removed.

that's why i looked at the socket package. the util package is a core module tho, so i wouldn't expect the webpack resolver to resolve it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging a pull request may close this issue.