170 lines
16 KiB
JavaScript
170 lines
16 KiB
JavaScript
|
'use strict';
|
||
|
|
||
|
var _path = require('path');
|
||
|
|
||
|
var _path2 = _interopRequireDefault(_path);
|
||
|
|
||
|
var _resolve = require('eslint-module-utils/resolve');
|
||
|
|
||
|
var _resolve2 = _interopRequireDefault(_resolve);
|
||
|
|
||
|
var _importType = require('../core/importType');
|
||
|
|
||
|
var _docsUrl = require('../docsUrl');
|
||
|
|
||
|
var _docsUrl2 = _interopRequireDefault(_docsUrl);
|
||
|
|
||
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||
|
|
||
|
const enumValues = { enum: ['always', 'ignorePackages', 'never'] };
|
||
|
const patternProperties = {
|
||
|
type: 'object',
|
||
|
patternProperties: { '.*': enumValues }
|
||
|
};
|
||
|
const properties = {
|
||
|
type: 'object',
|
||
|
properties: {
|
||
|
'pattern': patternProperties,
|
||
|
'ignorePackages': { type: 'boolean' }
|
||
|
}
|
||
|
};
|
||
|
|
||
|
function buildProperties(context) {
|
||
|
|
||
|
const result = {
|
||
|
defaultConfig: 'never',
|
||
|
pattern: {},
|
||
|
ignorePackages: false
|
||
|
};
|
||
|
|
||
|
context.options.forEach(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) {
|
||
|
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;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
module.exports = {
|
||
|
meta: {
|
||
|
type: 'suggestion',
|
||
|
docs: {
|
||
|
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 (context) {
|
||
|
|
||
|
const props = buildProperties(context);
|
||
|
|
||
|
function getModifier(extension) {
|
||
|
return props.pattern[extension] || props.defaultConfig;
|
||
|
}
|
||
|
|
||
|
function isUseOfExtensionRequired(extension, isPackageMain) {
|
||
|
return getModifier(extension) === 'always' && (!props.ignorePackages || !isPackageMain);
|
||
|
}
|
||
|
|
||
|
function isUseOfExtensionForbidden(extension) {
|
||
|
return getModifier(extension) === 'never';
|
||
|
}
|
||
|
|
||
|
function isResolvableWithoutExtension(file) {
|
||
|
const extension = _path2.default.extname(file);
|
||
|
const fileWithoutExtension = file.slice(0, -extension.length);
|
||
|
const resolvedFileWithoutExtension = (0, _resolve2.default)(fileWithoutExtension, context);
|
||
|
|
||
|
return resolvedFileWithoutExtension === (0, _resolve2.default)(file, context);
|
||
|
}
|
||
|
|
||
|
function checkFileExtension(node) {
|
||
|
const source = node.source;
|
||
|
|
||
|
// bail if the declaration doesn't have a source, e.g. "export { foo };"
|
||
|
|
||
|
if (!source) return;
|
||
|
|
||
|
const importPath = source.value;
|
||
|
|
||
|
// don't enforce anything on builtins
|
||
|
if ((0, _importType.isBuiltIn)(importPath, context.settings)) return;
|
||
|
|
||
|
const resolvedPath = (0, _resolve2.default)(importPath, context);
|
||
|
|
||
|
// get extension from resolved path, if possible.
|
||
|
// for unresolved, use source value.
|
||
|
const extension = _path2.default.extname(resolvedPath || importPath).substring(1);
|
||
|
|
||
|
// determine if this is a module
|
||
|
const isPackageMain = (0, _importType.isExternalModuleMain)(importPath, context.settings) || (0, _importType.isScopedMain)(importPath);
|
||
|
|
||
|
if (!extension || !importPath.endsWith(`.${extension}`)) {
|
||
|
const extensionRequired = isUseOfExtensionRequired(extension, isPackageMain);
|
||
|
const extensionForbidden = isUseOfExtensionForbidden(extension);
|
||
|
if (extensionRequired && !extensionForbidden) {
|
||
|
context.report({
|
||
|
node: source,
|
||
|
message: `Missing file extension ${extension ? `"${extension}" ` : ''}for "${importPath}"`
|
||
|
});
|
||
|
}
|
||
|
} else if (extension) {
|
||
|
if (isUseOfExtensionForbidden(extension) && isResolvableWithoutExtension(importPath)) {
|
||
|
context.report({
|
||
|
node: source,
|
||
|
message: `Unexpected use of file extension "${extension}" for "${importPath}"`
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return {
|
||
|
ImportDeclaration: checkFileExtension,
|
||
|
ExportNamedDeclaration: checkFileExtension
|
||
|
};
|
||
|
}
|
||
|
};
|
||
|
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9ydWxlcy9leHRlbnNpb25zLmpzIl0sIm5hbWVzIjpbImVudW1WYWx1ZXMiLCJlbnVtIiwicGF0dGVyblByb3BlcnRpZXMiLCJ0eXBlIiwicHJvcGVydGllcyIsImJ1aWxkUHJvcGVydGllcyIsImNvbnRleHQiLCJyZXN1bHQiLCJkZWZhdWx0Q29uZmlnIiwicGF0dGVybiIsImlnbm9yZVBhY2thZ2VzIiwib3B0aW9ucyIsImZvckVhY2giLCJvYmoiLCJ1bmRlZmluZWQiLCJPYmplY3QiLCJhc3NpZ24iLCJtb2R1bGUiLCJleHBvcnRzIiwibWV0YSIsImRvY3MiLCJ1cmwiLCJzY2hlbWEiLCJhbnlPZiIsIml0ZW1zIiwiYWRkaXRpb25hbEl0ZW1zIiwiY3JlYXRlIiwicHJvcHMiLCJnZXRNb2RpZmllciIsImV4dGVuc2lvbiIsImlzVXNlT2ZFeHRlbnNpb25SZXF1aXJlZCIsImlzUGFja2FnZU1haW4iLCJpc1VzZU9mRXh0ZW5zaW9uRm9yYmlkZGVuIiwiaXNSZXNvbHZhYmxlV2l0aG91dEV4dGVuc2lvbiIsImZpbGUiLCJwYXRoIiwiZXh0bmFtZSIsImZpbGVXaXRob3V0RXh0ZW5zaW9uIiwic2xpY2UiLCJsZW5ndGgiLCJyZXNvbHZlZEZpbGVXaXRob3V0RXh0ZW5zaW9uIiwiY2hlY2tGaWxlRXh0ZW5zaW9uIiwibm9kZSIsInNvdXJjZSIsImltcG9ydFBhdGgiLCJ2YWx1ZSIsInNldHRpbmdzIiwicmVzb2x2ZWRQYXRoIiwic3Vic3RyaW5nIiwiZW5kc1dpdGgiLCJleHRlbnNpb25SZXF1aXJlZCIsImV4dGVuc2lvbkZvcmJpZGRlbiIsInJlcG9ydCIsIm1lc3NhZ2UiLCJJbXBvcnREZWNsYXJhdGlvbiIsIkV4cG9ydE5hbWVkRGVjbGFyYXRpb24iXSwibWFwcGluZ3MiOiI7O0FBQUE7Ozs7QUFFQTs7OztBQUNBOztBQUNBOzs7Ozs7QUFFQSxNQUFNQSxhQUFhLEVBQUVDLE1BQU0sQ0FBRSxRQUFGLEVBQVksZ0JBQVosRUFBOEIsT0FB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
|