-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
add no-useless-path-segments rule Fixes #471 #912
Merged
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
9806019
add no-useless-path-segments rule Fixes #471
graingert 0b9e333
add failing test
graingert ec8bfca
normalize path first, then count '../'s
graingert 57eb999
path.normalize sometimes . eg when there's only one .
graingert 80eba07
add no-useless-path-segments fixer
graingert 46bc9ae
always use posix path for normalization
graingert d09bf2c
remove escape regexp
graingert 9fe6e9f
add test for double slashes
graingert 1e04bd1
use RegExp.prototype.test
graingert b69faca
Handle edge-case
danny-andrews b80a62f
Use resolve plugin
danny-andrews c080287
Fix typo
danny-andrews 3090e84
Add test case
danny-andrews File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
/** | ||
* @fileOverview Ensures that there are no useless path segments | ||
* @author Thomas Grainger | ||
*/ | ||
|
||
import path from 'path' | ||
import sumBy from 'lodash/sumBy' | ||
import resolve from 'eslint-module-utils/resolve' | ||
import moduleVisitor from 'eslint-module-utils/moduleVisitor' | ||
|
||
/** | ||
* convert a potentially relative path from node utils into a true | ||
* relative path. | ||
* | ||
* ../ -> .. | ||
* ./ -> . | ||
* .foo/bar -> ./.foo/bar | ||
* ..foo/bar -> ./..foo/bar | ||
* foo/bar -> ./foo/bar | ||
* | ||
* @param rel {string} relative posix path potentially missing leading './' | ||
* @returns {string} relative posix path that always starts with a ./ | ||
**/ | ||
function toRel(rel) { | ||
const stripped = rel.replace(/\/$/g, '') | ||
return /^((\.\.)|(\.))($|\/)/.test(stripped) ? stripped : `./${stripped}` | ||
} | ||
|
||
function normalize(fn) { | ||
return toRel(path.posix.normalize(fn)) | ||
} | ||
|
||
const countRelParent = x => sumBy(x, v => v === '..') | ||
|
||
module.exports = { | ||
meta: { fixable: 'code' }, | ||
|
||
create: function (context) { | ||
const currentDir = path.dirname(context.getFilename()) | ||
|
||
function checkSourceValue(source) { | ||
const { value } = source | ||
|
||
function report(proposed) { | ||
context.report({ | ||
node: source, | ||
message: `Useless path segments for "${value}", should be "${proposed}"`, | ||
fix: fixer => fixer.replaceText(source, JSON.stringify(proposed)), | ||
}) | ||
} | ||
|
||
if (!value.startsWith('.')) { | ||
return | ||
} | ||
|
||
const resolvedPath = resolve(value, context) | ||
const normed = normalize(value) | ||
if (normed !== value && resolvedPath === resolve(normed, context)) { | ||
return report(normed) | ||
} | ||
|
||
if (value.startsWith('./')) { | ||
return | ||
} | ||
|
||
if (resolvedPath === undefined) { | ||
return | ||
} | ||
|
||
const expected = path.relative(currentDir, resolvedPath) | ||
const expectedSplit = expected.split(path.sep) | ||
const valueSplit = value.replace(/^\.\//, '').split('/') | ||
const valueNRelParents = countRelParent(valueSplit) | ||
const expectedNRelParents = countRelParent(expectedSplit) | ||
const diff = valueNRelParents - expectedNRelParents | ||
|
||
if (diff <= 0) { | ||
return | ||
} | ||
|
||
return report( | ||
toRel(valueSplit | ||
.slice(0, expectedNRelParents) | ||
.concat(valueSplit.slice(valueNRelParents + diff)) | ||
.join('/')) | ||
) | ||
} | ||
|
||
return moduleVisitor(checkSourceValue, context.options[0]) | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export default 4 |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import { test } from '../utils' | ||
import { RuleTester } from 'eslint' | ||
|
||
const ruleTester = new RuleTester() | ||
const rule = require('rules/no-useless-path-segments') | ||
|
||
function runResolverTests(resolver) { | ||
ruleTester.run(`no-useless-path-segments (${resolver})`, rule, { | ||
valid: [ | ||
test({ code: 'import "./malformed.js"' }), | ||
test({ code: 'import "./test-module"' }), | ||
test({ code: 'import "./bar/"' }), | ||
test({ code: 'import "."' }), | ||
test({ code: 'import ".."' }), | ||
test({ code: 'import fs from "fs"' }), | ||
], | ||
|
||
invalid: [ | ||
test({ | ||
code: 'import "./../files/malformed.js"', | ||
errors: [ 'Useless path segments for "./../files/malformed.js", should be "../files/malformed.js"'], | ||
}), | ||
test({ | ||
code: 'import "./../files/malformed"', | ||
errors: [ 'Useless path segments for "./../files/malformed", should be "../files/malformed"'], | ||
}), | ||
test({ | ||
code: 'import "../files/malformed.js"', | ||
errors: [ 'Useless path segments for "../files/malformed.js", should be "./malformed.js"'], | ||
}), | ||
test({ | ||
code: 'import "../files/malformed"', | ||
errors: [ 'Useless path segments for "../files/malformed", should be "./malformed"'], | ||
}), | ||
test({ | ||
code: 'import "./test-module/"', | ||
errors: [ 'Useless path segments for "./test-module/", should be "./test-module"'], | ||
}), | ||
test({ | ||
code: 'import "./"', | ||
errors: [ 'Useless path segments for "./", should be "."'], | ||
}), | ||
test({ | ||
code: 'import "../"', | ||
errors: [ 'Useless path segments for "../", should be ".."'], | ||
}), | ||
test({ | ||
code: 'import "./deep//a"', | ||
errors: [ 'Useless path segments for "./deep//a", should be "./deep/a"'], | ||
}), | ||
], | ||
}) | ||
} | ||
|
||
['node', 'webpack'].forEach(runResolverTests) |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
We should test all of these cases:
foo/index.something
exists → path should be "foo"foo.something
exists → path should be "foo"foo/index.something
andfoo.something
exist →foo/
andfoo
are different, and the difference is important, and this rule should not warn on it.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 that should be handled by https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/extensions.md
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.
yes, that should handle whether there's an extension or not - i'm saying that "whether this rule can warn on a path or not" actually depends on what's on disk, because the trailing slash is meaningful.
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.
Can we add a test case for catching multiple slashes, e.g.
foo//something
? See #915 (comment).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.
That's a good idea.