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

[New]: add rule `default-import-match-filename`

Open golopot opened this issue 5 years ago • 18 comments

Closes #325 .

  • Import name is considered to matche filename if they are equal after toLowercase, and striping extensions, and striping characters "._-".
  • CommonJs is also checked.
  • Cases like "./", "../", are ~~not~~ now checked.

golopot avatar Sep 15 '19 16:09 golopot

Coverage Status

Coverage increased (+0.003%) to 97.841% when pulling 678fc02529afcf771d1a74cb57b42386721da90c on golopot:default-import-match-filename into 47f912e74eccbb6009ea5778a7cdb33e918dd495 on benmosher:master.

coveralls avatar Sep 15 '19 16:09 coveralls

What does the "exception" option looks like?

// When "name" and "path" both globly match an item in whitelist, the error is ignored.
{
  whitelist: [
    {
      name: '*', // any name is allowed
      path: '*/models/*',
    },
    {
      name: 'reactDomSever',
      path: 'react-dom/sever',
    },
  ];
}

golopot avatar Sep 17 '19 22:09 golopot

ping @golopot, it'd be great to get this in :-)

ljharb avatar Feb 14 '20 01:02 ljharb

Currently the rule is case-insesative, and underscores are ignored. Variable name foo_BarBAeZ is allowed for file foo_bar_baz.

https://github.com/benmosher/eslint-plugin-import/blob/dfdd7d43a06c1f449788a9921b2973c7f13e0eab/tests/src/rules/default-import-match-filename.js#L50-L53

golopot avatar Feb 15 '20 07:02 golopot

I am having trouble in designing the option ignorePatterns. This option is meant to ignore import statements when the import source matches a configured pattern. There are three choices:

  1. (Current implementation) Use the resolved absolute path to match against the patterns. For example, in import a from './foo', the source resolves to absolute path /home/me/my-project/src/foo.js, and that path is used to match against a pattern, say **/foo.js. In this case the pattern **/foo.js matches. This approach has the drawback that only patterns starting with **/ can ever match, and "relative" patterns like src/foo.js or foo.js will never match in general.

  2. Use the directory containing the closest package.json as the "cwd" for glob matching, With this approach, patterns src/foo.js or foo.js are considered to be relative to /home/me/my-project, which is the location of the closest package.json.

  3. Use process.cwd as the "cwd" for glob matching. This approach has the drawback that the lint result depends on the cwd used for running the linter.

golopot avatar Feb 29 '20 07:02 golopot

Typically, process.cwd() is the current dir - because a rule can't know if its config came from a file (or which one), or from command line config, or an inline comment.

With option 1, however - could that resolved path have process.cwd() stripped off it, and all patterns applied relative to that?

ljharb avatar Mar 10 '20 06:03 ljharb

With option 1, however - could that resolved path have process.cwd() stripped off it, and all patterns applied relative to that?

It should be doable using the function path.relative(). That sounds like option 3 in https://github.com/benmosher/eslint-plugin-import/pull/1476#issuecomment-592915191?

golopot avatar Mar 10 '20 12:03 golopot

Yes, option 3 sounds right to me.

ljharb avatar Mar 13 '20 06:03 ljharb

Looking forward to this rule. Is there any autofix provided? Or not for now ?

stropho avatar Mar 18 '20 17:03 stropho

It would be great to see a little bit more control over how this rule operates in the parserOptions. For example, the ability to make this case sensitive or the ability to whitelist which characters are ignored.

// These would be the defaults
{
    caseSensitive: false,
    allowedChars: [".", "_", "-"],
}

For example: My repo uses absolute imports and I would like to ensure that the component is imported exactly as the filename is styled. Lets say I have a component called GridColumn:

parserOptions: {
    caseSensitive: true,
    allowedChars: [],
}

pass - import GridColumn from "components/GridColumn"; fail - import gridColumn from "components/GridColumn"; fail - import Gridcolumn from "components/GridColumn"; fail - import gridcolumn from "components/GridColumn"; fail - import Grid-Column from "components/GridColumn"; fail - import Grid_Column from "components/GridColumn"; fail - import GRIDCOLUMN from "components/GridColumn"; etc...

edit: typo

Pearce-Ropion avatar May 09 '20 01:05 Pearce-Ropion

@Pearce-Ropion while the casing should ideally match, remember that not all filesystems are case-sensitive.

ljharb avatar Jun 01 '20 17:06 ljharb

@Pearce-Ropion while the casing should ideally match, remember that not all filesystems are case-sensitive.

Of course, thats why I suggested it as an optional setting. My reasoning here that some people have the luxury of knowing what systems their code is being developed on (for example, every engineer at my company is required to use a linux machine). Also, after having a glance at the code, it looks like the isCompatible() fn is doing a string equality check which would make it relatively easy to add these options.

Suggested changes snippet

/**
 * @param {string} filename
 * @param {object} options
 * @returns {string}
 */
function normalizeFilename(filename, options) {
  const allowedCharsRegex = new RegExp(`[${options.allowedChars.map(c => `\${c}`)}]`, 'g');

  if (options.caseSensitive) {
    return filename.replace(allowedCharsRegex, '');
  }

  return filename.replace(allowedCharsRegex, '').toLowerCase()
}

/**
 * Test if local name matches filename.
 * @param {string} localName
 * @param {string} filename
 * @param {object} options
 * @returns {boolean}
 */
function isCompatible(localName, filename, options) {
  const allowedCharsRegex = new RegExp(`[${options.allowedChars.map(c => `\${c}`)}]`, 'g');

  let normalizedLocalName = localName.replace(allowedCharsRegex, '');
  if (!options.caseSensitive) {
    normalizedLocalName = normalizedLocalName.toLowerCase();
  }

  return (
    normalizedLocalName === normalizeFilename(filename, options) ||
    normalizedLocalName === normalizeFilename(removeExtension(filename), options)
  )
}

Pearce-Ropion avatar Jun 01 '20 17:06 Pearce-Ropion

It will be awesome to have this rule 👍 Let's merge this thing 🚀

granmoe avatar Aug 11 '20 20:08 granmoe

https://github.com/benmosher/eslint-plugin-import/issues/325#issuecomment-280840161

I think that should be part of this. When would you ever want to enforce this for an import name and not the export name, granted it's an internal module.

epiqueras avatar Aug 11 '20 22:08 epiqueras

Does this need further work or is it done?

fregante avatar Jul 20 '21 03:07 fregante

It needs further work, as far as I'm aware. I'll mark it as a draft.

ljharb avatar Jul 20 '21 05:07 ljharb

I'm also working on an NPM package. But is quite some work because you need lots of options for customization. Will write when it's published.

dword-design avatar Jul 20 '21 08:07 dword-design

Is there any update on this? It would be great to have this as part of eslint-plugin-import IMO

giladgd avatar Apr 24 '23 17:04 giladgd