eslint-plugin-flowtype
Version:
Flowtype linting rules for ESLint.
223 lines (190 loc) • 6.31 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
const groups = {
function: 'function',
intersection: 'intersection',
keyword: 'keyword',
literal: 'literal',
named: 'named',
nullish: 'nullish',
object: 'object',
tuple: 'tuple',
union: 'union',
unknown: 'unknown'
}; // eslint-disable-next-line complexity
const getGroup = node => {
// eslint-disable-next-line default-case
switch (node.type) {
case 'FunctionTypeAnnotation':
return groups.function;
case 'IntersectionTypeAnnotation':
return groups.intersection;
case 'AnyTypeAnnotation':
case 'BooleanTypeAnnotation':
case 'NumberTypeAnnotation':
case 'StringTypeAnnotation':
case 'SymbolTypeAnnotation':
case 'ThisTypeAnnotation':
return groups.keyword;
case 'NullLiteralTypeAnnotation':
case 'NullableTypeAnnotation':
case 'VoidTypeAnnotation':
return groups.nullish;
case 'BooleanLiteralTypeAnnotation':
case 'NumberLiteralTypeAnnotation':
case 'StringLiteralTypeAnnotation':
return groups.literal;
case 'ArrayTypeAnnotation':
case 'IndexedAccessType':
case 'GenericTypeAnnotation':
case 'OptionalIndexedAccessType':
return groups.named;
case 'ObjectTypeAnnotation':
return groups.object;
case 'TupleTypeAnnotation':
return groups.tuple;
case 'UnionTypeAnnotation':
return groups.union;
}
return groups.unknown;
};
const fallbackSort = (a, b) => {
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
return 0;
};
const sorters = {
asc: (collator, a, b) => {
return collator.compare(a, b) || fallbackSort(a, b);
},
desc: (collator, a, b) => {
return collator.compare(b, a) || fallbackSort(b, a);
}
};
const create = context => {
const sourceCode = context.getSourceCode();
const {
checkIntersections = true,
checkUnions = true,
groupOrder = [groups.keyword, groups.named, groups.literal, groups.function, groups.object, groups.tuple, groups.intersection, groups.union, groups.nullish],
order = 'asc'
} = context.options[1] || {};
const sort = sorters[order];
const collator = new Intl.Collator('en', {
numeric: true,
sensitivity: 'base'
});
const checkSorting = node => {
const sourceOrder = node.types.map(type => {
var _groupOrder$indexOf;
const group = (_groupOrder$indexOf = groupOrder === null || groupOrder === void 0 ? void 0 : groupOrder.indexOf(getGroup(type))) !== null && _groupOrder$indexOf !== void 0 ? _groupOrder$indexOf : -1;
return {
group: group === -1 ? Number.MAX_SAFE_INTEGER : group,
node: type,
text: sourceCode.getText(type)
};
});
const expectedOrder = [...sourceOrder].sort((a, b) => {
if (a.group !== b.group) {
return a.group - b.group;
}
return sort(collator, a.text, b.text);
});
const hasComments = node.types.some(type => {
const count = sourceCode.getCommentsBefore(type).length + sourceCode.getCommentsAfter(type).length;
return count > 0;
});
let prev = null;
for (let i = 0; i < expectedOrder.length; i += 1) {
const type = node.type === 'UnionTypeAnnotation' ? 'union' : 'intersection';
const current = sourceOrder[i].text;
const last = prev; // keep track of the last token
prev = current || last;
if (!last || !current) {
continue;
}
if (expectedOrder[i].node !== sourceOrder[i].node) {
const data = {
current,
last,
order,
type
};
const fix = fixer => {
const sorted = expectedOrder.map(t => {
return t.text;
}).join(node.type === 'UnionTypeAnnotation' ? ' | ' : ' & ');
return fixer.replaceText(node, sorted);
};
context.report(_objectSpread({
data,
messageId: 'notSorted',
node
}, hasComments ? {
suggest: [{
fix,
messageId: 'suggestFix'
}]
} : {
fix
}));
}
}
};
return {
IntersectionTypeAnnotation(node) {
if (checkIntersections === true) {
checkSorting(node);
}
},
UnionTypeAnnotation(node) {
if (checkUnions === true) {
checkSorting(node);
}
}
};
};
var _default = {
create,
meta: {
fixable: 'code',
messages: {
notSorted: 'Expected {{type}} members to be in {{order}}ending order. "{{current}}" should be before "{{last}}".',
suggestFix: 'Sort members of type (removes all comments).'
},
schema: [{
properties: {
checkIntersections: {
type: 'boolean'
},
checkUnions: {
type: 'boolean'
},
groupOrder: {
items: {
enum: Object.keys(groups),
type: 'string'
},
type: 'array'
},
order: {
enum: ['asc', 'desc'],
type: 'string'
}
},
type: 'object'
}]
}
};
exports.default = _default;
module.exports = exports.default;
;