242 lines
25 KiB
JavaScript
242 lines
25 KiB
JavaScript
|
|
'use strict';var _path = require('path');var _path2 = _interopRequireDefault(_path);
|
||
|
|
|
||
|
|
var _minimatch = require('minimatch');var _minimatch2 = _interopRequireDefault(_minimatch);
|
||
|
|
var _resolve = require('eslint-module-utils/resolve');var _resolve2 = _interopRequireDefault(_resolve);
|
||
|
|
var _importType = require('../core/importType');
|
||
|
|
var _moduleVisitor = require('eslint-module-utils/moduleVisitor');var _moduleVisitor2 = _interopRequireDefault(_moduleVisitor);
|
||
|
|
var _docsUrl = require('../docsUrl');var _docsUrl2 = _interopRequireDefault(_docsUrl);function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { 'default': obj };}
|
||
|
|
|
||
|
|
var enumValues = { 'enum': ['always', 'ignorePackages', 'never'] };
|
||
|
|
var patternProperties = {
|
||
|
|
type: 'object',
|
||
|
|
patternProperties: { '.*': enumValues } };
|
||
|
|
|
||
|
|
var properties = {
|
||
|
|
type: 'object',
|
||
|
|
properties: {
|
||
|
|
pattern: patternProperties,
|
||
|
|
checkTypeImports: { type: 'boolean' },
|
||
|
|
ignorePackages: { type: 'boolean' },
|
||
|
|
pathGroupOverrides: {
|
||
|
|
type: 'array',
|
||
|
|
items: {
|
||
|
|
type: 'object',
|
||
|
|
properties: {
|
||
|
|
pattern: {
|
||
|
|
type: 'string' },
|
||
|
|
|
||
|
|
patternOptions: {
|
||
|
|
type: 'object' },
|
||
|
|
|
||
|
|
action: {
|
||
|
|
type: 'string',
|
||
|
|
'enum': ['enforce', 'ignore'] } },
|
||
|
|
|
||
|
|
|
||
|
|
additionalProperties: false,
|
||
|
|
required: ['pattern', 'action'] } } } };
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
function buildProperties(context) {
|
||
|
|
|
||
|
|
var result = {
|
||
|
|
defaultConfig: 'never',
|
||
|
|
pattern: {},
|
||
|
|
ignorePackages: false };
|
||
|
|
|
||
|
|
|
||
|
|
context.options.forEach(function (obj) {
|
||
|
|
|
||
|
|
// If this is a string, set defaultConfig to its value
|
||
|
|
if (typeof obj === 'string') {
|
||
|
|
result.defaultConfig = obj;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// If this is not the new structure, transfer all props to result.pattern
|
||
|
|
if (obj.pattern === undefined && obj.ignorePackages === undefined && obj.checkTypeImports === undefined) {
|
||
|
|
Object.assign(result.pattern, obj);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// If pattern is provided, transfer all props
|
||
|
|
if (obj.pattern !== undefined) {
|
||
|
|
Object.assign(result.pattern, obj.pattern);
|
||
|
|
}
|
||
|
|
|
||
|
|
// If ignorePackages is provided, transfer it to result
|
||
|
|
if (obj.ignorePackages !== undefined) {
|
||
|
|
result.ignorePackages = obj.ignorePackages;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (obj.checkTypeImports !== undefined) {
|
||
|
|
result.checkTypeImports = obj.checkTypeImports;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (obj.pathGroupOverrides !== undefined) {
|
||
|
|
result.pathGroupOverrides = obj.pathGroupOverrides;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
if (result.defaultConfig === 'ignorePackages') {
|
||
|
|
result.defaultConfig = 'always';
|
||
|
|
result.ignorePackages = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
module.exports = {
|
||
|
|
meta: {
|
||
|
|
type: 'suggestion',
|
||
|
|
docs: {
|
||
|
|
category: 'Style guide',
|
||
|
|
description: 'Ensure consistent use of file extension within the import path.',
|
||
|
|
url: (0, _docsUrl2['default'])('extensions') },
|
||
|
|
|
||
|
|
|
||
|
|
schema: {
|
||
|
|
anyOf: [
|
||
|
|
{
|
||
|
|
type: 'array',
|
||
|
|
items: [enumValues],
|
||
|
|
additionalItems: false },
|
||
|
|
|
||
|
|
{
|
||
|
|
type: 'array',
|
||
|
|
items: [
|
||
|
|
enumValues,
|
||
|
|
properties],
|
||
|
|
|
||
|
|
additionalItems: false },
|
||
|
|
|
||
|
|
{
|
||
|
|
type: 'array',
|
||
|
|
items: [properties],
|
||
|
|
additionalItems: false },
|
||
|
|
|
||
|
|
{
|
||
|
|
type: 'array',
|
||
|
|
items: [patternProperties],
|
||
|
|
additionalItems: false },
|
||
|
|
|
||
|
|
{
|
||
|
|
type: 'array',
|
||
|
|
items: [
|
||
|
|
enumValues,
|
||
|
|
patternProperties],
|
||
|
|
|
||
|
|
additionalItems: false }] } },
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
create: function () {function create(context) {
|
||
|
|
|
||
|
|
var props = buildProperties(context);
|
||
|
|
|
||
|
|
function getModifier(extension) {
|
||
|
|
return props.pattern[extension] || props.defaultConfig;
|
||
|
|
}
|
||
|
|
|
||
|
|
function isUseOfExtensionRequired(extension, isPackage) {
|
||
|
|
return getModifier(extension) === 'always' && (!props.ignorePackages || !isPackage);
|
||
|
|
}
|
||
|
|
|
||
|
|
function isUseOfExtensionForbidden(extension) {
|
||
|
|
return getModifier(extension) === 'never';
|
||
|
|
}
|
||
|
|
|
||
|
|
function isResolvableWithoutExtension(file) {
|
||
|
|
var extension = _path2['default'].extname(file);
|
||
|
|
var fileWithoutExtension = file.slice(0, -extension.length);
|
||
|
|
var resolvedFileWithoutExtension = (0, _resolve2['default'])(fileWithoutExtension, context);
|
||
|
|
|
||
|
|
return resolvedFileWithoutExtension === (0, _resolve2['default'])(file, context);
|
||
|
|
}
|
||
|
|
|
||
|
|
function isExternalRootModule(file) {
|
||
|
|
if (file === '.' || file === '..') {return false;}
|
||
|
|
var slashCount = file.split('/').length - 1;
|
||
|
|
|
||
|
|
if (slashCount === 0) {return true;}
|
||
|
|
if ((0, _importType.isScoped)(file) && slashCount <= 1) {return true;}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
function computeOverrideAction(pathGroupOverrides, path) {
|
||
|
|
for (var i = 0, l = pathGroupOverrides.length; i < l; i++) {var _pathGroupOverrides$i =
|
||
|
|
pathGroupOverrides[i],pattern = _pathGroupOverrides$i.pattern,patternOptions = _pathGroupOverrides$i.patternOptions,action = _pathGroupOverrides$i.action;
|
||
|
|
if ((0, _minimatch2['default'])(path, pattern, patternOptions || { nocomment: true })) {
|
||
|
|
return action;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function checkFileExtension(source, node) {
|
||
|
|
// bail if the declaration doesn't have a source, e.g. "export { foo };", or if it's only partially typed like in an editor
|
||
|
|
if (!source || !source.value) {return;}
|
||
|
|
|
||
|
|
var importPathWithQueryString = source.value;
|
||
|
|
|
||
|
|
// If not undefined, the user decided if rules are enforced on this import
|
||
|
|
var overrideAction = computeOverrideAction(
|
||
|
|
props.pathGroupOverrides || [],
|
||
|
|
importPathWithQueryString);
|
||
|
|
|
||
|
|
|
||
|
|
if (overrideAction === 'ignore') {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// don't enforce anything on builtins
|
||
|
|
if (!overrideAction && (0, _importType.isBuiltIn)(importPathWithQueryString, context.settings)) {return;}
|
||
|
|
|
||
|
|
var importPath = importPathWithQueryString.replace(/\?(.*)$/, '');
|
||
|
|
|
||
|
|
// don't enforce in root external packages as they may have names with `.js`.
|
||
|
|
// Like `import Decimal from decimal.js`)
|
||
|
|
if (!overrideAction && isExternalRootModule(importPath)) {return;}
|
||
|
|
|
||
|
|
var resolvedPath = (0, _resolve2['default'])(importPath, context);
|
||
|
|
|
||
|
|
// get extension from resolved path, if possible.
|
||
|
|
// for unresolved, use source value.
|
||
|
|
var extension = _path2['default'].extname(resolvedPath || importPath).substring(1);
|
||
|
|
|
||
|
|
// determine if this is a module
|
||
|
|
var isPackage = (0, _importType.isExternalModule)(
|
||
|
|
importPath,
|
||
|
|
(0, _resolve2['default'])(importPath, context),
|
||
|
|
context) ||
|
||
|
|
(0, _importType.isScoped)(importPath);
|
||
|
|
|
||
|
|
if (!extension || !importPath.endsWith('.' + String(extension))) {
|
||
|
|
// ignore type-only imports and exports
|
||
|
|
if (!props.checkTypeImports && (node.importKind === 'type' || node.exportKind === 'type')) {return;}
|
||
|
|
var extensionRequired = isUseOfExtensionRequired(extension, !overrideAction && isPackage);
|
||
|
|
var extensionForbidden = isUseOfExtensionForbidden(extension);
|
||
|
|
if (extensionRequired && !extensionForbidden) {
|
||
|
|
context.report({
|
||
|
|
node: source,
|
||
|
|
message: 'Missing file extension ' + (
|
||
|
|
extension ? '"' + String(extension) + '" ' : '') + 'for "' + String(importPathWithQueryString) + '"' });
|
||
|
|
|
||
|
|
}
|
||
|
|
} else if (extension) {
|
||
|
|
if (isUseOfExtensionForbidden(extension) && isResolvableWithoutExtension(importPath)) {
|
||
|
|
context.report({
|
||
|
|
node: source,
|
||
|
|
message: 'Unexpected use of file extension "' + String(extension) + '" for "' + String(importPathWithQueryString) + '"' });
|
||
|
|
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return (0, _moduleVisitor2['default'])(checkFileExtension, { commonjs: true });
|
||
|
|
}return create;}() };
|
||
|
|
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9ydWxlcy9leHRlbnNpb25zLmpzIl0sIm5hbWVzIjpbImVudW1WYWx1ZXMiLCJwYXR0ZXJuUHJvcGVydGllcyIsInR5cGUiLCJwcm9wZXJ0aWVzIiwicGF0dGVybiIsImNoZWNrVHlwZUltcG9ydHMiLCJpZ25vcmVQYWNrYWdlcyIsInBhdGhHcm91cE92ZXJyaWRlcyIsIml0ZW1zIiwicGF0dGVybk9wdGlvbnMiLCJhY3Rpb24iLCJhZGRpdGlvbmFsUHJvcGVydGllcyIsInJlcXVpcmVkIiwiYnVpbGRQcm9wZXJ0aWVzIiwiY29udGV4dCIsInJlc3VsdCIsImRlZmF1bHRDb25maWciLCJvcHRpb25zIiwiZm9yRWFjaCIsIm9iaiIsInVuZGVmaW5lZCIsIk9iamVjdCIsImFzc2lnbiIsIm1vZHVsZSIsImV4cG9ydHMiLCJtZXRhIiwiZG9jcyIsImNhdGVnb3J5IiwiZGVzY3JpcHRpb24iLCJ1cmwiLCJzY2hlbWEiLCJhbnlPZiIsImFkZGl0aW9uYWxJdGVtcyIsImNyZWF0ZSIsInByb3BzIiwiZ2V0TW9kaWZpZXIiLCJleHRlbnNpb24iLCJpc1VzZU9mRXh0ZW5zaW9uUmVxdWlyZWQiLCJpc1BhY2thZ2UiLCJpc1VzZU9mRXh0ZW5zaW9uRm9yYmlkZGVuIiwiaXNSZXNvbHZhYmxlV2l0aG91dEV4dGVuc2lvbiIsImZpbGUiLCJwYXRoIiwiZXh0bmFtZSIsImZpbGVXaXRob3V0RXh0ZW5zaW9uIiwic2xpY2UiLCJsZW5ndGgiLCJyZXNvbHZlZEZpbGVXaXRob3V0RXh0ZW5zaW9uIiwiaXNFeHRlcm5hbFJvb3RNb2R1bGUiLCJzbGFzaENvdW50Iiwic3BsaXQiLCJjb21wdXRlT3ZlcnJpZGVBY3Rpb24iLCJpIiwibCIsIm5vY29tbWVudCIsImNoZWNrRmlsZUV4dGVuc2lvbiIsInNvdXJjZSIsIm5vZGUiLCJ2YWx1ZSIsImltcG9ydFBhdGhXaXRoUXVlcnlTdHJpbmciLCJvdmVycmlkZUFjdGlvbiIsInNldHRpbmdzIiwiaW1wb3J0UGF0aCIsInJlcGxhY2UiLCJyZXNvbHZlZFBhdGgiLCJzdWJzdHJpbmciLCJlbmRzV2l0aCIsImltcG9ydEtpbmQiLCJleHBvcnRLaW5kIiwiZXh0ZW5zaW9uUmVxdWlyZWQiLCJleHRlbnNpb25Gb3JiaWRkZW4iLCJyZXBvcnQiLCJtZXNzYWdlIiwiY29tbW9uanMiXSwibWFwcGluZ3MiOiJhQUFBLDRCOztBQUVBLHNDO0FBQ0Esc0Q7QUFDQTtBQUNBLGtFO0FBQ0EscUM7O0FBRUEsSUFBTUEsYUFBYSxFQUFFLFFBQU0sQ0FBQyxRQUFELEVBQVcsZ0JBQVgsRUFBNkIsT0FBN0IsQ0FBUixFQUFuQjtBQUNBLElBQU1DLG9CQUFvQjtBQUN4QkMsUUFBTSxRQURrQjtBQUV4QkQscUJBQW1CLEVBQUUsTUFBTUQsVUFBUixFQUZLLEVBQTFCOztBQUlBLElBQU1HLGFBQWE7QUFDakJELFFBQU0sUUFEVztBQUVqQkMsY0FBWTtBQUNWQyxhQUFTSCxpQkFEQztBQUVWSSxzQkFBa0IsRUFBRUgsTUFBTSxTQUFSLEVBRlI7QUFHVkksb0JBQWdCLEVBQUVKLE1BQU0sU0FBUixFQUhOO0FBSVZLLHdCQUFvQjtBQUNsQkwsWUFBTSxPQURZO0FBRWxCTSxhQUFPO0FBQ0xOLGNBQU0sUUFERDtBQUVMQyxvQkFBWTtBQUNWQyxtQkFBUztBQUNQRixrQkFBTSxRQURDLEVBREM7O0FBSVZPLDBCQUFnQjtBQUNkUCxrQkFBTSxRQURRLEVBSk47O0FBT1ZRLGtCQUFRO0FBQ05SLGtCQUFNLFFBREE7QUFFTixvQkFBTSxDQUFDLFNBQUQsRUFBWSxRQUFaLENBRkEsRUFQRSxFQUZQOzs7QUFjTFMsOEJBQXNCLEtBZGpCO0FBZUxDLGtCQUFVLENBQUMsU0FBRCxFQUFZLFFBQVosQ0FmTCxFQUZXLEVBSlYsRUFGSyxFQUFuQjs7Ozs7O0FBNkJBLFNBQVNDLGVBQVQsQ0FBeUJDLE9BQXpCLEVBQWtDOztBQUVoQyxNQUFNQyxTQUFTO0FBQ2JDLG1CQUFlLE9BREY7QUFFYlosYUFBUyxFQUZJO0FBR2JFLG9CQUFnQixLQUhILEVBQWY7OztBQU1BUSxVQUFRRyxPQUFSLENBQWdCQyxPQUFoQixDQUF3QixVQUFDQyxHQUFELEVBQVM7O0FBRS9CO0FBQ0EsUUFBSSxPQUFPQSxHQUFQLEtBQWUsUUFBbkIsRUFBNkI7QUFDM0JKLGFBQU9DLGFBQVAsR0FBdUJHLEdBQXZCO0FBQ0E7QUFDRDs7QUFFRDtBQUNBLFFBQUlBLElBQUlmLE9BQUosS0FBZ0JnQixTQUFoQixJQUE2QkQsSUFBSWIsY0FBSixLQUF1QmMsU0FBcEQsSUFBaUVELElBQUlkLGdCQUFKLEtBQXlCZSxTQUE5RixFQUF5RztBQUN2R0MsYUFBT0MsTUFBUCxDQUFjUCxPQUFPWCxPQUFyQixFQUE4QmUsR0FBOUI7QUFDQTtBQUNEOztBQUVEO0FBQ0EsUUFBSUEsSUFBSWYsT0FBSixLQUFnQmdCLFNBQXBCLEVBQStCO0FBQzdCQyxhQUFPQyxNQUFQLENBQWNQLE9BQU9YLE9BQXJCLEVBQThCZSxJQUFJZixPQUFsQztBQUNEOztBQUVEO0FBQ0EsUUFBSWUsSUFBSWIsY0FBSixLQUF1QmMsU0FBM0IsRUFBc0M7QUFDcENMLGFBQU9ULGNBQVAsR0FBd0JhLElBQUliLGNBQTVCO0FBQ0Q7O0FBRUQsUUFBSWEsSUFBSWQsZ0JBQUosS0FBeUJlLFNBQTdCLEVBQXdDO0FBQ3RDTCxhQUFPVixnQkFBUCxHQUEwQmMsSUFBSWQsZ0JBQTlCO0FBQ0Q7O0FBRUQsUUFBSWMsSUFBSVosa0JBQUosS0FBMkJhLFNBQS9CLEVBQTBDO0FBQ3hDTCxhQUFPUixrQkFBUCxHQUE0QlksSUFBSVosa0JBQWhDO0FBQ0Q7QUFDRixHQS9CRDs7QUFpQ0EsTUFBSVEsT0FBT0MsYUFBUCxLQUF5QixnQkFBN0IsRUFBK0M7QUFDN0NELFdBQU9DLGFBQVAsR0FBdUIsUUFBdkI7QUFDQUQsV0FBT1QsY0FBUCxHQUF3QixJQUF4QjtBQUNEOztBQUVELFNBQU9TLE1BQVA7QUFDRDs7QUFFRFEsT0FBT0MsT0FBUCxHQUFpQjtBQUNmQyxRQUFNO0FBQ0p2QixVQUFNLFlBREY7QUFFSndCLFVBQU07QUFDSkMsZ0JBQVUsYUFETjtBQUVKQyxtQkFBYSxpRUFGVDtBQUdKQyxXQUFLLDBCQUFRLFlBQVIsQ0FIRCxFQUZGOzs7QUFRSkMsWUFBUTtBQUNOQyxhQUFPO0FBQ0w7QUFDRTdCLGNBQU0sT0FEUjtBQUVFTSxlQUFPLENBQUNSLFVBQUQsQ0FGVDtBQUdFZ0MseUJBQWlCLEtBSG5CLEVBREs7O0FBTUw7QUFDRTlCLGNBQU0sT0FEUjtBQUVFTSxlQUFPO0FBQ0xSLGtCQURLO0FBRUxHLGtCQUZLLENBRlQ7O0FBTUU2Qix5QkFBaUIsS0FObkIsRUFOSzs7QUFjTDtBQUNFOUIsY0FBTSxPQURSO0FBRUVNLGVBQU8sQ0FBQ0wsVUFBRCxDQUZUO0FBR0U2Qix5QkFBaUIsS0FIbkIsRUFkSzs7QUFtQkw7QUFDRTlCLGN
|