eslint-plugin-import icon indicating copy to clipboard operation
eslint-plugin-import copied to clipboard

Error when importing images using aliases

Open javierguzman opened this issue 4 years ago • 34 comments

Hello all,

Recently I have updated my project and my eslint has started complaining when I import an image using an alias (extraneous dependency error). In order to get rid of this error I added: "import/ignore": ["/*.png/", "/*.jpg/"]

Which made my webpack build is ok as expected. However, VSCode highlights my "import myimage from @Images/my_image.png" as red due to "import/no-extraneous-dependencies".

On the other hand if I have: "import/ignore":["*.png", "*.jpg"]

Then the highlight red error from VSCode goes away but my webpack build throws:

ERROR in Invalid regular expression: *.png: Nothing to repeat
Occurred while linting blabla\index.js:2

It looks like VSCode ESLint plugin and webpack eslint stuff do not use the same patterns for import/ignore.

I have created the following repo where I get the same result: https://github.com/javierguzman/eslint-error

You may need to do npm install --force for the node_modules.

In order to reproduce the error:

  1. Open Header.tsx and you will see no error highlights
  2. Run "npm run build:production"
  3. You get the invalid regular expression
  4. Change the expression of import/ignore
  5. Run "npm run build:production" and it is ok
  6. However if you go to Header.tsx you will see the import marked as red due to extraneous dependencies

I hope my configuration is not that bad :smile: I have two eslint configuration, one for development and one for production. build:production as well as vscode uses the production es lint configuration. This can be seen in the vscode configuration folder under the repository I have created.

Please, let me know if you need something else from me.

Thank you in advance for the help and regards

p.s. the lint configuration to be looked at is .eslintrc.json, ignore the development one

javierguzman avatar Sep 19 '21 07:09 javierguzman

Why is webpack running eslint at all? The build process should be entirely separate from testing.

ljharb avatar Sep 19 '21 14:09 ljharb

I’m also very confused why you’d have two linting configurations. Production code is the build output, this is never linted.

ljharb avatar Sep 19 '21 15:09 ljharb

I come from embedded software and I have learned everything about web apps, etc. on my own so I might not be doing stuff properly.

Having said that, the package called "eslint-webpack-plugin" has 2m downloads on npm and if I am not mistaken its purpose is to run eslint when running webpack, if I have not missed anything, I am not the only one. Or am I mistaken?

I have two configurations because when I am in develop mode I am more flexible as I usually play around a ton. But let's stick with .eslintrc.json as is the one I am using when calling build:production and the one used in the vscode configuration. I think I forgot to mention that.

javierguzman avatar Sep 19 '21 17:09 javierguzman

You’re not the only one, of course, but that doesn't mean it’s the right way to do it :-)

So with the production config, which theoretically runs in the cli, vscode, and así webpack, you’re getting different results? Note that the CLI is the source of truth.

ljharb avatar Sep 19 '21 18:09 ljharb

How can I run eslint cli directly? Now I think about it, the import/ignore should be a glob, shouldn't it? That means VSCode plugin has the correct behavior as when I type '*png', the red hightline goes away, as expected. However, if I leave like that, it is webpack the one complaining saying "invalid regular expression when I am building. So it must be eslint-webpack-plugin the one reading wrongly the field. Does this make sense?

javierguzman avatar Sep 20 '21 13:09 javierguzman

Yes, that makes perfect sense to me.

to run eslint directly, make sure it’s installed as a dev dep (it shouldn’t ever be global), and then run npx eslint .. You can put eslint . in an npm run-script as well - i usually make one called “lint” and then another called “pretest” that runs npm run lint.

ljharb avatar Sep 20 '21 15:09 ljharb

This is interesting, I got the same "webpack" error while running eslint cli:

❯ npx eslint ./src/* -c ./config/.eslintrc.json

Oops! Something went wrong! :(

ESLint: 7.32.0

SyntaxError: Invalid regular expression: /*.png/: Nothing to repeat
Occurred while linting MYHIDDENPATH\client\src\index.js:2
    at new RegExp (<anonymous>)
    at ignore (MYHIDDENPATH\client\node_modules\eslint-module-utils\ignore.js:47:19)
    at Function.ExportMap.for (MYHIDDENPATH\client\node_modules\eslint-plugin-import\lib\ExportMap.js:739:31)
    at Function.ExportMap.get (MYHIDDENPATH\client\node_modules\eslint-plugin-import\lib\ExportMap.js:734:463)
    at checkSpecifiers (MYHIDDENPATH\client\node_modules\eslint-plugin-import\lib\rules\named.js:42:46)
    at MYHIDDENPATH\client\node_modules\eslint\lib\linter\safe-emitter.js:45:58
    at Array.forEach (<anonymous>)
    at Object.emit (MYHIDDENPATH\client\node_modules\eslint\lib\linter\safe-emitter.js:45:38)
    at NodeEventGenerator.applySelector (MYHIDDENPATH\client\node_modules\eslint\lib\linter\node-event-generator.js:293:26)
    at NodeEventGenerator.applySelectors (MYHIDDENPATH\client\node_modules\eslint\lib\linter\node-event-generator.js:322:22)

javierguzman avatar Sep 20 '21 15:09 javierguzman

ok, that's great - it means it's reproducible.

What are the contents of that eslint config?

ljharb avatar Sep 20 '21 19:09 ljharb

Did you miss my link in my first message? I thought you knew already it was reproducible as I created a repository having the same issue. The content of the eslint config is in that repository: https://github.com/javierguzman/eslint-error/blob/main/client/config/.eslintrc.json

javierguzman avatar Sep 20 '21 19:09 javierguzman

oh sorry, i'll try to use that repo.

ljharb avatar Sep 20 '21 23:09 ljharb

got it reproduced, thanks :-)

ljharb avatar Sep 21 '21 16:09 ljharb

@javierguzman ok so turns out import/ignore is actually a regexp pattern, and "*.png" isn't a valid one because of both the star and the dot. Changing it to ["\\.png$", "\\.jpg$"] seems to make it correctly produce a lint warning.

ljharb avatar Sep 21 '21 16:09 ljharb

yes, I got that behavior using "import/ignore": ["/*.png/", "/*.jpg/"] However, if you open Header.tsx file you should get a red line over import LOGO from '@Images/facebook.png';

https://github.com/javierguzman/eslint-error/blob/main/client/src/Features/Header/Header.tsx

So if the correct one is a regexp, then it is the vscode eslint plugin the one not behaving properly...as it hightlights the import of a png file as red when we have specified to ignore those ones.

javierguzman avatar Sep 21 '21 19:09 javierguzman

["/*.png/", "/*.jpg/"] is incorrect as well, since new RegExp strings are not supposed to be bounded by forward slashes.

Change it to ["\\.png$", "\\.jpg$"] and it should be fine.

The current warning is because it thinks @Images/facebook.png is a package - because that's a valid package name. While aliases are best when they can't be a valid package name, either way, you need a custom resolver to be able to resolve @Images/ so that this plugin can know where it points.

ljharb avatar Sep 21 '21 23:09 ljharb

Then that is a bug, isn't? I mean if I use ["/*.png/", /*.jpg/"] the behavior I see is the same as with ["\\.png$", "\\.jpg$"]

On the other hand, I resolve my images in the webpack-common.js file: https://github.com/javierguzman/eslint-error/blob/main/client/config/webpack-common.js

Here I have:

const resolve = {
    modules: [srcPath, 'node_modules'],
    alias: {
      '@Header': path.resolve(srcPath, 'Features', 'Header'),
      '@Common': path.resolve(rootPath, '..', 'common'),
      '@Images': path.resolve(rootPath, '..', 'common', 'assets', 'images')
    },
    extensions: ['.ts', '.js', '.tsx', '.jsx'],
    fallback: { path: false }
  };

javierguzman avatar Sep 22 '21 06:09 javierguzman

no - /*.png/ in a regex means "a slash, 0 or more times, then any character, then png, then a slash".

If you're resolving those with webpack, then you must use the webpack resolver for eslint-plugin-import: https://github.com/import-js/eslint-plugin-import#resolvers

ljharb avatar Sep 22 '21 06:09 ljharb

Oh I see, I thought you were saying ["/.png/", /.jpg/"] is not a valid regex, but you meant it is a valid regex but not the one I was looking for 😄

I have the webpack resolver as well:

"settings": {
        "import/resolver": {
            "node" : {},// due to https://github.com/benmosher/eslint-plugin-import/issues/1396
            "webpack": {
                "config" : "config/webpack-prod.js",
                "extensions": [".js", ".jsx", ".ts", ".tsx"]
            }
        }

javierguzman avatar Sep 22 '21 06:09 javierguzman

You should always have the node resolver - not just due to that closed issue - but the webpack resolver should be listed first.

ljharb avatar Sep 22 '21 06:09 ljharb

I had always node first and it has been working fine. Just in case I have placed webpack stuff first and eslint becomes "crazy" as it tries to look for webpack-prod.js under the node_modules, I guess tue to the filepath.toLowerCase message ( I have even restarted eslint server & vscode without success):

[Info  - 8:37:45] ESLint library loaded from: blabla\client\node_modules\eslint\lib\api.js
[Info  - 8:37:48] filepath.toLowerCase is not a function Occurred while linting blabla\client\config\webpack-common.js:1
Error resolving webpackConfig Error: Cannot find module 'blabla\client\node_modules\react-bootstrap\config\webpack-prod.js'
Require stack:
- blabla\client\node_modules\eslint-import-resolver-webpack\index.js
- blabla\client\node_modules\eslint-module-utils\resolve.js
- blabla\client\node_modules\eslint-plugin-import\lib\rules\no-unresolved.js
- blabla\client\node_modules\eslint-plugin-import\lib\index.js
- blabla\client\node_modules\@eslint\eslintrc\lib\config-array-factory.js
- blabla\client\node_modules\@eslint\eslintrc\lib\index.js
- blabla\client\node_modules\eslint\lib\cli-engine\cli-engine.js
- blabla\client\node_modules\eslint\lib\cli-engine\index.js
- blabla\client\node_modules\eslint\lib\api.js
- c:\Users\javie\.vscode\extensions\dbaeumer.vscode-eslint-2.1.25\server\out\eslintServer.js
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:934:15)
    at Module._load (internal/modules/cjs/loader.js:779:27)
    at Function.f._load (electron/js2c/asar_bundle.js:5:12913)
    at Module.require (internal/modules/cjs/loader.js:1006:19)
    at require (internal/modules/cjs/helpers.js:88:18)
    at Object.exports.resolve (blabla\client\node_modules\eslint-import-resolver-webpack\index.js:86:25)
    at v2 (blabla\client\node_modules\eslint-module-utils\resolve.js:117:23)
    at withResolver (blabla\client\node_modules\eslint-module-utils\resolve.js:122:14)
    at fullResolve (blabla\client\node_modules\eslint-module-utils\resolve.js:139:22)
    at Function.relative (blabla\client\node_modules\eslint-module-utils\resolve.js:84:10) {
  code: 'MODULE_NOT_FOUND',
  requireStack: [
    'blabla\\client\\node_modules\\eslint-import-resolver-webpack\\index.js',
    'blabla\\client\\node_modules\\eslint-module-utils\\resolve.js',
    'blabla\\client\\node_modules\\eslint-plugin-import\\lib\\rules\\no-unresolved.js',
    'blabla\\client\\node_modules\\eslint-plugin-import\\lib\\index.js',
    'blabla\\client\\node_modules\\@eslint\\eslintrc\\lib\\config-array-factory.js',
    'blabla\\client\\node_modules\\@eslint\\eslintrc\\lib\\index.js',
    'blabla\\client\\node_modules\\eslint\\lib\\cli-engine\\cli-engine.js',
    'blabla\\client\\node_modules\\eslint\\lib\\cli-engine\\index.js',
    'blabla\\client\\node_modules\\eslint\\lib\\api.js',
    'myuser\\.vscode\\extensions\\dbaeumer.vscode-eslint-2.1.25\\server\\out\\eslintServer.js'
  ]
}

javierguzman avatar Sep 22 '21 06:09 javierguzman

Try ./config/webpack-prod.js? (note the leading dot)

ljharb avatar Sep 22 '21 06:09 ljharb

It seems better but the message "[Info - 9:32:30] filepath.toLowerCase is not a function Occurred while linting " still appears and makes no difference in the whole behavior. The alias image import is still hightlighted as red. Bear in mind other imports do not have this problem. Right now, it is like the import/ignore is not read properly by vscode eslint plugin. I am going to open an issue in plugin repo as well to check if they can add something.

javierguzman avatar Sep 22 '21 08:09 javierguzman

I have run again npx eslint ./src/features/Header/Header.tsx -c ./config/.eslintrc.json and still complains about the extraneous dependency, so it is not the plugin I believe.

javierguzman avatar Sep 22 '21 12:09 javierguzman

Hello @ljharb do you any other ideas? I am still facing the same issue. When I mentioned before "is not the plugin I believe" I meant the VSCode plugin.

javierguzman avatar Sep 24 '21 03:09 javierguzman

I don't yet have any other ideas; I'll keep playing with the repo.

ljharb avatar Sep 24 '21 04:09 ljharb

Any news about this @ljharb ?

javierguzman avatar Sep 30 '21 11:09 javierguzman

Hello @ljharb , is there someone else who might be able to give a hand? or any other channels? I would not like to see this issue forgotten in the darkness. Thank you in advance.

javierguzman avatar Oct 11 '21 12:10 javierguzman

Anyone who wants can help; there’s no other maintainers in practical terms. It remains on my list.

ljharb avatar Oct 11 '21 16:10 ljharb

And is there any Discord or Slack channels for ESLint? I am just asking because I imagine you might be overwhelmed being the only one around here trying to help everybody. Being ESLint such a popular ecosystem does not help either :smile:

javierguzman avatar Oct 12 '21 08:10 javierguzman

I have continued looking at this and I believe the problem could be summarized to "How to make an alias to an outside project folder".

If I import a TS file from @Common then I get a similar error. However, if I remove the alias and use relative paths ( a ton of ../../) then it works ok.

From the tests I have made, I think the trick is to have properly webpack configuration resolve.alias section (https://github.com/javierguzman/eslint-error/blob/main/client/config/webpack-common.js) AND tsconfig.json(https://github.com/javierguzman/eslint-error/blob/main/client/tsconfig.json). However, for now I have not discovered what it is missing.

If someone can spot the problem I am all ears.

javierguzman avatar Oct 16 '21 09:10 javierguzman

Yes, webpack aliases should work if you’re using the webpack resolver and webpack is configured correctly. If you’re using TS then tsconfig also has to have the same info.

ljharb avatar Oct 16 '21 15:10 ljharb