-
Notifications
You must be signed in to change notification settings - Fork 18
Adds no-ancestor-directory-import
rule
#149
Conversation
85bd95c
to
1a6c03e
Compare
|
||
## Rule Details | ||
|
||
This rule aims to... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this sentence incomplete?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kaelig updated
1a6c03e
to
008aefc
Compare
278b76f
to
e6d1fa9
Compare
@@ -0,0 +1,27 @@ | |||
# Prefer relative local imports extend to the file from where they are importing without relying on an index file. (prefer-explicit-local-imports) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I might describe this more as "no ancestor directory import".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, all makes sense. I like that name.
}, | ||
}; | ||
|
||
function isVagueImport(str) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It isn't really vague, it's more about the fact that it is a direct ancestor.
create(context) { | ||
return { | ||
ImportDeclaration(node) { | ||
const pathParts = node.source.value.split('/'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use path.sep
return str === '' || str === '.' || str === '..'; | ||
} | ||
|
||
return basename(str, extname(str)) === 'index'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you are better off resolving the path and then checking that either it is a directory and an ancestor of the current path, or it is an index file of such a directory. This prevents potential issues with this method, as this incorrectly warns on some weirder import paths that we'd still consider technically valid
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some weirder import paths that we'd still consider technically valid
What would some of these cases be? It would be good to add them to the tests
}, | ||
], | ||
|
||
invalid: [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a few valid and invalid tests for importing non-default exports
eg. import {Foo, Bar} from '../'
CHANGELOG.md
Outdated
@@ -14,6 +14,7 @@ | |||
### Added | |||
* `shopify/prefer-singular-enums` ([#132](https://github.com/Shopify/eslint-plugin-shopify/pull/132)) | |||
* `shopify/react-no-multiple-render-methods` ([#134](https://github.com/Shopify/eslint-plugin-shopify/pull/134)) | |||
* `shopify/prefer-explicit-local-imports` ([#149](https://github.com/Shopify/eslint-plugin-shopify/pull/149)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shopify/prefer-namespaced-imports
might be a better name - not entirely sure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 Nice one. Nothing major from my end, just a couple comments.
8f22080
to
1723535
Compare
prefer-explicit-local-imports
ruleno-ancestor-directory-import
rule
@lemonmade ready for another look. Thanks! cc/ @BPScott since you ran into this recently. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice. A few more test cases around components reaching into sibling components, and subcomponents would be handy. If I've understood the purpose of this PR then the following should all pass:
{
code: "import Bar from '../Bar'",
filename: fixtureFile('basic-app/app/components/Foo/Foo.js'),
},
{
code: "import Bar from '..'",
filename: fixtureFile('basic-app/app/components/Foo/Foo.js'),
},
{
code: "import Bar from '../Bar'",
filename: fixtureFile('basic-app/app/components/Foo/components/Baz/Baz.js'),
},
{
code: "import Qux from '..'",
filename: fixtureFile('basic-app/app/components/Foo/components/Baz/Baz.js'),
},
{
code: "import Qux from '../Qux'",
filename: fixtureFile('basic-app/app/components/Foo/components/Baz/Baz.js'),
},
{
code: "import Bar from '../../../Bar'",
filename: fixtureFile('basic-app/app/components/Foo/components/Baz/Baz.js'),
},
{
code: "import Bar from '../../..'",
filename: fixtureFile('basic-app/app/components/Foo/components/Baz/Baz.js'),
},
const {fixtureFile} = require('../../utilities'); | ||
const rule = require('../../../lib/rules/no-ancestor-directory-import'); | ||
|
||
const ruleTester = new RuleTester(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can do
const parserOptions =
new RuleTester({parserOptions: {
ecmaVersion: 6,
sourceType: 'module',
}})
to avoid having to pass parserOptions into every valid/invalid block below. (see https://eslint.org/docs/developer-guide/nodejs-api#ruletester)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TIL! Thanks @BPScott
@@ -0,0 +1,27 @@ | |||
# Prefer imports from within a directory extend to the file from where they are importing without relying on an index file. (no-ancestor-directory-import) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefer that
meta: { | ||
docs: { | ||
description: | ||
'Prefer imports from within a directory extend to the file from where they are importing without relying on an index file.', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here too
docs: { | ||
description: | ||
'Prefer imports from within a directory extend to the file from where they are importing without relying on an index file.', | ||
category: 'Best Practises', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Best practices
fixable: null, | ||
}, | ||
create(context) { | ||
function isAncestorDirectoryImport(resolvedSource) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function doesn't seem to quite line up with what it checks
e417285
to
e820e7a
Compare
@@ -0,0 +1,27 @@ | |||
# Prefer that imports from within a directory extend to the file from where they are importing without relying on an index file. (no-ancestor-directory-import) | |||
|
|||
Imports inside the same directory should extend directly to the file from where they are importing without relying on an index file. This means the source of these imports should not end with a directory (`/`), but the path should terminate at an individual filename. This preserves the index file inside a directory as a mechanism for exposing pieces of the module to the outside application. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The last sentence should be augmented with , rather than as a way to export the parts that the module internally depends on.
or something like that.
context.report({ | ||
node, | ||
message: | ||
'Relative local imports should extend to the file from where they are importing without relying on an index file.', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if this message is quite right; since we resolve the source, it might not even be a relative import (i.e., something in app/sections
asking for index
would fail this rule, as that likely resolves to app/index.js
resolvedSource, | ||
); | ||
|
||
const parts = relativeDifference |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this just strips out all the leading ./../../
stuff right?
filename: fixtureFile('basic-app/app/components/Foo/Foo.js'), | ||
}, | ||
{ | ||
code: "import Bar from '../../..'", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might want to check that ../../../
works also (trailing slash)
ecce01b
to
e257b2d
Compare
9434d5b
to
7b8a75a
Compare
7b8a75a
to
414db1d
Compare
Closes #138
In addition to the code, feedback on the description and name of this rule would be really great. I found it to be a hard one to describe in words.