Skip to content

Commit

Permalink
normalize path first, then count '../'s
Browse files Browse the repository at this point in the history
avoid problems with ./ham/ -> ./ham/index.js by simply
consuming '..', path segment pairs.
  • Loading branch information
Thomas Grainger committed Aug 8, 2017
1 parent 41b2bb6 commit fa304ad
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 13 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
"eslint-module-utils": "^2.1.1",
"has": "^1.0.1",
"lodash.cond": "^4.3.0",
"lodash.sumby": "^4.3.0",
"minimatch": "^3.0.3",
"read-pkg-up": "^2.0.0"
},
Expand Down
52 changes: 41 additions & 11 deletions src/rules/no-useless-path-segments.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,20 @@
*/

import path from 'path'
import sumBy from 'lodash.sumby'
import resolve from 'eslint-module-utils/resolve'
import moduleVisitor from 'eslint-module-utils/moduleVisitor'

function relative(from, to) {
const rel = path.relative(from, to)
return rel.startsWith('.') ? rel : `.${path.sep}${rel}`
function toRel(rel, sep) {
return rel.startsWith(`..${sep}`) ? rel : `.${sep}${rel}`
}

function normalize(fn, sep) {
return toRel(path.normalize(fn), sep)
}

const countRelParent = x => sumBy(x, v => v === '..')

module.exports = {
meta: {},

Expand All @@ -20,25 +26,49 @@ module.exports = {

function checkSourceValue(source) {
const { value } = source

function report(proposed) {
context.report({
node: source,
message: `Useless path segments for "${value}", should be "${proposed}"`,
})
}

if (!value.startsWith('.')) {
return
}

const normed = normalize(value, '/')
if (normed !== value) {
return report(normed)
}

if (value.startsWith('./')) {
return
}

const resolvedPath = resolve(value, context)
if (resolvedPath === undefined) {
return
}

const expected = path.parse(relative(currentDir, resolvedPath))
const valueParsed = path.parse(value)
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 (valueParsed.dir !== expected.dir) {
const proposed = path.format({ dir: expected.dir, base: valueParsed.base })
context.report({
node: source,
message: `Useless path segments for "${value}", should be "${proposed}"`,
})
if (diff <= 0) {
return
}

return report(
toRel(valueSplit
.slice(0, expectedNRelParents)
.concat(valueSplit.slice(valueNRelParents + diff))
.join('/'), '/')
)
}

return moduleVisitor(checkSourceValue, context.options[0])
Expand Down
12 changes: 10 additions & 2 deletions tests/src/rules/no-useless-path-segments.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,19 @@ function runResolverTests(resolver) {
invalid: [
test({
code: 'import "./../files/malformed.js"',
errors: [ 'Useless path segments for "./../files/malformed.js", should be "./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 "./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"'],
}),
],
})
Expand Down

0 comments on commit fa304ad

Please sign in to comment.