Skip to content

Commit

Permalink
Merge 3090e84 into fa02415
Browse files Browse the repository at this point in the history
  • Loading branch information
graingert committed Dec 19, 2017
2 parents fa02415 + 3090e84 commit 85d1659
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const rules = {
'no-dynamic-require': require('./rules/no-dynamic-require'),
'unambiguous': require('./rules/unambiguous'),
'no-unassigned-import': require('./rules/no-unassigned-import'),
'no-useless-path-segments': require('./rules/no-useless-path-segments'),

// export
'exports-last': require('./rules/exports-last'),
Expand Down
91 changes: 91 additions & 0 deletions src/rules/no-useless-path-segments.js
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])
},
}
1 change: 1 addition & 0 deletions tests/files/bar/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default 4
Empty file added tests/files/index.js
Empty file.
55 changes: 55 additions & 0 deletions tests/src/rules/no-useless-path-segments.js
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)

0 comments on commit 85d1659

Please sign in to comment.