2020-08-25 23:57:08 +00:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var _ignore = require('eslint-module-utils/ignore');
|
|
|
|
var _moduleVisitor = require('eslint-module-utils/moduleVisitor');var _moduleVisitor2 = _interopRequireDefault(_moduleVisitor);
|
|
|
|
var _resolve = require('eslint-module-utils/resolve');var _resolve2 = _interopRequireDefault(_resolve);
|
|
|
|
var _path = require('path');var _path2 = _interopRequireDefault(_path);
|
2022-11-10 10:43:16 +00:00
|
|
|
var _docsUrl = require('../docsUrl');var _docsUrl2 = _interopRequireDefault(_docsUrl);function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { 'default': obj };}
|
2020-08-25 23:57:08 +00:00
|
|
|
|
|
|
|
/**
|
2022-11-10 10:43:16 +00:00
|
|
|
* 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 relativePath {string} relative posix path potentially missing leading './'
|
|
|
|
* @returns {string} relative posix path that always starts with a ./
|
|
|
|
**/
|
2020-08-25 23:57:08 +00:00
|
|
|
function toRelativePath(relativePath) {
|
2022-11-10 10:43:16 +00:00
|
|
|
var stripped = relativePath.replace(/\/$/g, ''); // Remove trailing /
|
2020-08-25 23:57:08 +00:00
|
|
|
|
2022-11-10 10:43:16 +00:00
|
|
|
return (/^((\.\.)|(\.))($|\/)/.test(stripped) ? stripped : './' + String(stripped));
|
2020-08-25 23:57:08 +00:00
|
|
|
} /**
|
|
|
|
* @fileOverview Ensures that there are no useless path segments
|
|
|
|
* @author Thomas Grainger
|
2022-11-10 10:43:16 +00:00
|
|
|
*/function normalize(fn) {return toRelativePath(_path2['default'].posix.normalize(fn));
|
2020-08-25 23:57:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function countRelativeParents(pathSegments) {
|
2022-11-10 10:43:16 +00:00
|
|
|
return pathSegments.reduce(function (sum, pathSegment) {return pathSegment === '..' ? sum + 1 : sum;}, 0);
|
2020-08-25 23:57:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
meta: {
|
|
|
|
type: 'suggestion',
|
|
|
|
docs: {
|
2022-11-10 10:43:16 +00:00
|
|
|
url: (0, _docsUrl2['default'])('no-useless-path-segments') },
|
2020-08-25 23:57:08 +00:00
|
|
|
|
|
|
|
|
|
|
|
fixable: 'code',
|
|
|
|
|
|
|
|
schema: [
|
|
|
|
{
|
|
|
|
type: 'object',
|
|
|
|
properties: {
|
|
|
|
commonjs: { type: 'boolean' },
|
|
|
|
noUselessIndex: { type: 'boolean' } },
|
|
|
|
|
|
|
|
additionalProperties: false }] },
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2022-11-10 10:43:16 +00:00
|
|
|
create: function () {function create(context) {
|
|
|
|
var currentDir = _path2['default'].dirname(context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename());
|
|
|
|
var options = context.options[0];
|
2020-08-25 23:57:08 +00:00
|
|
|
|
2022-11-10 10:43:16 +00:00
|
|
|
function checkSourceValue(source) {var
|
|
|
|
importPath = source.value;
|
2020-08-25 23:57:08 +00:00
|
|
|
|
2022-11-10 10:43:16 +00:00
|
|
|
function reportWithProposedPath(proposedPath) {
|
|
|
|
context.report({
|
|
|
|
node: source,
|
|
|
|
// Note: Using messageIds is not possible due to the support for ESLint 2 and 3
|
|
|
|
message: 'Useless path segments for "' + String(importPath) + '", should be "' + String(proposedPath) + '"',
|
|
|
|
fix: function () {function fix(fixer) {return proposedPath && fixer.replaceText(source, JSON.stringify(proposedPath));}return fix;}() });
|
2020-08-25 23:57:08 +00:00
|
|
|
|
2022-11-10 10:43:16 +00:00
|
|
|
}
|
2020-08-25 23:57:08 +00:00
|
|
|
|
2022-11-10 10:43:16 +00:00
|
|
|
// Only relative imports are relevant for this rule --> Skip checking
|
|
|
|
if (!importPath.startsWith('.')) {
|
|
|
|
return;
|
|
|
|
}
|
2020-08-25 23:57:08 +00:00
|
|
|
|
2022-11-10 10:43:16 +00:00
|
|
|
// Report rule violation if path is not the shortest possible
|
|
|
|
var resolvedPath = (0, _resolve2['default'])(importPath, context);
|
|
|
|
var normedPath = normalize(importPath);
|
|
|
|
var resolvedNormedPath = (0, _resolve2['default'])(normedPath, context);
|
|
|
|
if (normedPath !== importPath && resolvedPath === resolvedNormedPath) {
|
|
|
|
return reportWithProposedPath(normedPath);
|
|
|
|
}
|
2020-08-25 23:57:08 +00:00
|
|
|
|
2022-11-10 10:43:16 +00:00
|
|
|
var fileExtensions = (0, _ignore.getFileExtensions)(context.settings);
|
|
|
|
var regexUnnecessaryIndex = new RegExp('.*\\/index(\\' + String(
|
|
|
|
Array.from(fileExtensions).join('|\\')) + ')?$');
|
2020-08-25 23:57:08 +00:00
|
|
|
|
|
|
|
|
2022-11-10 10:43:16 +00:00
|
|
|
// Check if path contains unnecessary index (including a configured extension)
|
|
|
|
if (options && options.noUselessIndex && regexUnnecessaryIndex.test(importPath)) {
|
|
|
|
var parentDirectory = _path2['default'].dirname(importPath);
|
2020-08-25 23:57:08 +00:00
|
|
|
|
2022-11-10 10:43:16 +00:00
|
|
|
// Try to find ambiguous imports
|
|
|
|
if (parentDirectory !== '.' && parentDirectory !== '..') {var _iteratorNormalCompletion = true;var _didIteratorError = false;var _iteratorError = undefined;try {
|
|
|
|
for (var _iterator = fileExtensions[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {var fileExtension = _step.value;
|
|
|
|
if ((0, _resolve2['default'])('' + String(parentDirectory) + String(fileExtension), context)) {
|
|
|
|
return reportWithProposedPath(String(parentDirectory) + '/');
|
|
|
|
}
|
|
|
|
}} catch (err) {_didIteratorError = true;_iteratorError = err;} finally {try {if (!_iteratorNormalCompletion && _iterator['return']) {_iterator['return']();}} finally {if (_didIteratorError) {throw _iteratorError;}}}
|
2020-08-25 23:57:08 +00:00
|
|
|
}
|
|
|
|
|
2022-11-10 10:43:16 +00:00
|
|
|
return reportWithProposedPath(parentDirectory);
|
|
|
|
}
|
2020-08-25 23:57:08 +00:00
|
|
|
|
2022-11-10 10:43:16 +00:00
|
|
|
// Path is shortest possible + starts from the current directory --> Return directly
|
|
|
|
if (importPath.startsWith('./')) {
|
|
|
|
return;
|
|
|
|
}
|
2020-08-25 23:57:08 +00:00
|
|
|
|
2022-11-10 10:43:16 +00:00
|
|
|
// Path is not existing --> Return directly (following code requires path to be defined)
|
|
|
|
if (resolvedPath === undefined) {
|
|
|
|
return;
|
|
|
|
}
|
2020-08-25 23:57:08 +00:00
|
|
|
|
2022-11-10 10:43:16 +00:00
|
|
|
var expected = _path2['default'].relative(currentDir, resolvedPath); // Expected import path
|
|
|
|
var expectedSplit = expected.split(_path2['default'].sep); // Split by / or \ (depending on OS)
|
|
|
|
var importPathSplit = importPath.replace(/^\.\//, '').split('/');
|
|
|
|
var countImportPathRelativeParents = countRelativeParents(importPathSplit);
|
|
|
|
var countExpectedRelativeParents = countRelativeParents(expectedSplit);
|
|
|
|
var diff = countImportPathRelativeParents - countExpectedRelativeParents;
|
2020-08-25 23:57:08 +00:00
|
|
|
|
2022-11-10 10:43:16 +00:00
|
|
|
// Same number of relative parents --> Paths are the same --> Return directly
|
|
|
|
if (diff <= 0) {
|
|
|
|
return;
|
|
|
|
}
|
2020-08-25 23:57:08 +00:00
|
|
|
|
2022-11-10 10:43:16 +00:00
|
|
|
// Report and propose minimal number of required relative parents
|
|
|
|
return reportWithProposedPath(
|
|
|
|
toRelativePath(
|
|
|
|
importPathSplit.
|
|
|
|
slice(0, countExpectedRelativeParents).
|
|
|
|
concat(importPathSplit.slice(countImportPathRelativeParents + diff)).
|
|
|
|
join('/')));
|
2020-08-25 23:57:08 +00:00
|
|
|
|
|
|
|
|
2022-11-10 10:43:16 +00:00
|
|
|
}
|
2020-08-25 23:57:08 +00:00
|
|
|
|
2022-11-10 10:43:16 +00:00
|
|
|
return (0, _moduleVisitor2['default'])(checkSourceValue, options);
|
|
|
|
}return create;}() };
|
|
|
|
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9ydWxlcy9uby11c2VsZXNzLXBhdGgtc2VnbWVudHMuanMiXSwibmFtZXMiOlsidG9SZWxhdGl2ZVBhdGgiLCJyZWxhdGl2ZVBhdGgiLCJzdHJpcHBlZCIsInJlcGxhY2UiLCJ0ZXN0Iiwibm9ybWFsaXplIiwiZm4iLCJwYXRoIiwicG9zaXgiLCJjb3VudFJlbGF0aXZlUGFyZW50cyIsInBhdGhTZWdtZW50cyIsInJlZHVjZSIsInN1bSIsInBhdGhTZWdtZW50IiwibW9kdWxlIiwiZXhwb3J0cyIsIm1ldGEiLCJ0eXBlIiwiZG9jcyIsInVybCIsImZpeGFibGUiLCJzY2hlbWEiLCJwcm9wZXJ0aWVzIiwiY29tbW9uanMiLCJub1VzZWxlc3NJbmRleCIsImFkZGl0aW9uYWxQcm9wZXJ0aWVzIiwiY3JlYXRlIiwiY29udGV4dCIsImN1cnJlbnREaXIiLCJkaXJuYW1lIiwiZ2V0UGh5c2ljYWxGaWxlbmFtZSIsImdldEZpbGVuYW1lIiwib3B0aW9ucyIsImNoZWNrU291cmNlVmFsdWUiLCJzb3VyY2UiLCJpbXBvcnRQYXRoIiwidmFsdWUiLCJyZXBvcnRXaXRoUHJvcG9zZWRQYXRoIiwicHJvcG9zZWRQYXRoIiwicmVwb3J0Iiwibm9kZSIsIm1lc3NhZ2UiLCJmaXgiLCJmaXhlciIsInJlcGxhY2VUZXh0IiwiSlNPTiIsInN0cmluZ2lmeSIsInN0YXJ0c1dpdGgiLCJyZXNvbHZlZFBhdGgiLCJub3JtZWRQYXRoIiwicmVzb2x2ZWROb3JtZWRQYXRoIiwiZmlsZUV4dGVuc2lvbnMiLCJzZXR0aW5ncyIsInJlZ2V4VW5uZWNlc3NhcnlJbmRleCIsIlJlZ0V4cCIsIkFycmF5IiwiZnJvbSIsImpvaW4iLCJwYXJlbnREaXJlY3RvcnkiLCJmaWxlRXh0ZW5zaW9uIiwidW5kZWZpbmVkIiwiZXhwZWN0ZWQiLCJyZWxhdGl2ZSIsImV4cGVjdGVkU3BsaXQiLCJzcGxpdCIsInNlcCIsImltcG9ydFBhdGhTcGxpdCIsImNvdW50SW1wb3J0UGF0aFJlbGF0aXZlUGFyZW50cyIsImNvdW50RXhwZWN0ZWRSZWxhdGl2ZVBhcmVudHMiLCJkaWZmIiwic2xpY2UiLCJjb25jYXQiXSwibWFwcGluZ3MiOiI7Ozs7O0FBS0E7QUFDQSxrRTtBQUNBLHNEO0FBQ0EsNEI7QUFDQSxxQzs7QUFFQTs7Ozs7Ozs7Ozs7OztBQWFBLFNBQVNBLGNBQVQsQ0FBd0JDLFlBQXhCLEVBQXNDO0FBQ3BDLE1BQU1DLFdBQVdELGFBQWFFLE9BQWIsQ0FBcUIsTUFBckIsRUFBNkIsRUFBN0IsQ0FBakIsQ0FEb0MsQ0FDZTs7QUFFbkQsU0FBTyx3QkFBdUJDLElBQXZCLENBQTRCRixRQUE1QixJQUF3Q0EsUUFBeEMsaUJBQXdEQSxRQUF4RCxDQUFQO0FBQ0QsQyxDQTVCRDs7O0tBOEJBLFNBQVNHLFNBQVQsQ0FBbUJDLEVBQW5CLEVBQXVCLENBQ3JCLE9BQU9OLGVBQWVPLGtCQUFLQyxLQUFMLENBQVdILFNBQVgsQ0FBcUJDLEVBQXJCLENBQWYsQ0FBUDtBQUNEOztBQUVELFNBQVNHLG9CQUFULENBQThCQyxZQUE5QixFQUE0QztBQUMxQyxTQUFPQSxhQUFhQyxNQUFiLENBQW9CLFVBQUNDLEdBQUQsRUFBTUMsV0FBTixVQUFzQkEsZ0JBQWdCLElBQWhCLEdBQXVCRCxNQUFNLENBQTdCLEdBQWlDQSxHQUF2RCxFQUFwQixFQUFnRixDQUFoRixDQUFQO0FBQ0Q7O0FBRURFLE9BQU9DLE9BQVAsR0FBaUI7QUFDZkMsUUFBTTtBQUNKQyxVQUFNLFlBREY7QUFFSkMsVUFBTTtBQUNKQyxXQUFLLDBCQUFRLDBCQUFSLENBREQsRUFGRjs7O0FBTUpDLGFBQVMsTUFOTDs7QUFRSkMsWUFBUTtBQUNOO0FBQ0VKLFlBQU0sUUFEUjtBQUVFSyxrQkFBWTtBQUNWQyxrQkFBVSxFQUFFTixNQUFNLFNBQVIsRUFEQTtBQUVWTyx3QkFBZ0IsRUFBRVAsTUFBTSxTQUFSLEVBRk4sRUFGZDs7QUFNRVEsNEJBQXNCLEtBTnhCLEVBRE0sQ0FSSixFQURTOzs7OztBQXFCZkMsUUFyQmUsK0JBcUJSQyxPQXJCUSxFQXFCQztBQUNkLFVBQU1DLGFBQWFyQixrQkFBS3NCLE9BQUwsQ0FBYUYsUUFBUUcsbUJBQVIsR0FBOEJILFFBQVFHLG1CQUFSLEVBQTlCLEdBQThESCxRQUFRSSxXQUFSLEVBQTNFLENBQW5CO0FBQ0EsVUFBTUMsVUFBVUwsUUFBUUssT0FBUixDQUFnQixDQUFoQixDQUFoQjs7QUFFQSxlQUFTQyxnQkFBVCxDQUEwQkMsTUFBMUIsRUFBa0M7QUFDakJDLGtCQURpQixHQUNGRCxNQURFLENBQ3hCRSxLQUR3Qjs7QUFHaEMsaUJBQVNDLHNCQUFULENBQWdDQyxZQUFoQyxFQUE4QztBQUM1Q1gsa0JBQVFZLE1BQVIsQ0FBZTtBQUNiQyxrQkFBTU4sTUFETztBQUViO0FBQ0FPLDREQUF1Q04sVUFBdkMsOEJBQWtFRyxZQUFsRSxPQUhhO0FBSWJJLDhCQUFLLDRCQUFTSixnQkFBZ0JLLE1BQU1DLFdBQU4sQ0FBa0JWLE1BQWxCLEVBQTBCVyxLQUFLQyxTQUFMLENBQWVSLFlBQWYsQ0FBMUIsQ0FBekIsRUFBTCxjQUphLEVBQWY7O0FBTUQ7O0FBRUQ7QUFDQSxZQUFJLENBQUNILFdBQVdZLFVBQVgsQ0FBc0IsR0FBdEIsQ0FBTCxFQUFpQztBQUMvQjtBQUNEOztBQUVEO0FBQ0EsWUFBTUMsZUFBZSwwQkFBUWIsVUFBUixFQUFvQlIsT0FBcEIsQ0FBckI7QUFDQSxZQUFNc0IsYUFBYTVDLFVBQVU4QixVQUFWLENBQW5CO0FBQ0EsWUFBTWUscUJBQXFCLDBCQUFRRCxVQUFSLEVBQW9CdEIsT0FBcEIsQ0FBM0I7QUFDQSxZQUFJc0IsZUFBZWQsVUFBZixJQUE2QmEsaUJBQWlCRSxrQkFBbEQsRUFBc0U7QUFDcEUsaUJBQU9iLHVCQUF1QlksVUFBdkIsQ0FBUDtBQUNEOztBQUVELFlBQU1FLGlCQUFpQiwrQkFBa0J4QixRQUFReUIsUUFBMUIsQ0FBdkI7QUFDQSxZQUFNQyx3QkFBd0IsSUFBSUMsTUFBSjtBQUNaQyxjQUFNQyxJQUFOLENBQVdMLGNBQVgsRUFBMkJNLElBQTNCLENBQWdDLEtBQWhDLENBRFksVUFBOUI7OztBQUlBO0FBQ0EsWUFBSXpCLFdBQVdBLFFBQVFSLGNBQW5CLElBQXFDNkIsc0JBQXNCakQsSUFBdEIsQ0FBMkIrQixVQUEzQixDQUF6QyxFQUFpRjtBQUMvRSxjQUFNdUIsa0JBQWtCbkQsa0JBQUtzQixPQUFMLENBQWFNLFVBQWIsQ0FBeEI7O0FBRUE7QUFDQSxjQUFJdUIsb0JBQW9CLEdBQXBCLElBQTJCQSxvQkFBb0IsSUFBbkQsRUFBeUQ7QUFDdkQsbUNBQTRCUCxjQUE1Qiw4SEFBNEMsS0FBakNRLGFBQWlDO0FBQzFDLG9CQUFJLHNDQUFXRCxlQUFYLFdBQTZCQyxhQUE3QixHQUE4Q2hDLE9BQTlDLENBQUosRUFBNEQ7QUFDMUQseUJBQU9VLDhCQUEwQnFCLGV
|