mirror of
https://github.com/Sevichecc/Urara-Blog.git
synced 2025-05-02 19:59:31 +08:00
570 lines
No EOL
20 KiB
Text
570 lines
No EOL
20 KiB
Text
"use strict";
|
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
if (k2 === undefined) k2 = k;
|
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
}
|
|
Object.defineProperty(o, k2, desc);
|
|
}) : (function(o, m, k, k2) {
|
|
if (k2 === undefined) k2 = k;
|
|
o[k2] = m[k];
|
|
}));
|
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
}) : function(o, v) {
|
|
o["default"] = v;
|
|
});
|
|
var __importStar = (this && this.__importStar) || function (mod) {
|
|
if (mod && mod.__esModule) return mod;
|
|
var result = {};
|
|
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
__setModuleDefault(result, mod);
|
|
return result;
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.defaultOrder = void 0;
|
|
const utils_1 = require("@typescript-eslint/utils");
|
|
const util = __importStar(require("../util"));
|
|
const neverConfig = {
|
|
type: 'string',
|
|
enum: ['never'],
|
|
};
|
|
const arrayConfig = (memberTypes) => ({
|
|
type: 'array',
|
|
items: {
|
|
oneOf: [
|
|
{
|
|
enum: memberTypes,
|
|
},
|
|
{
|
|
type: 'array',
|
|
items: {
|
|
enum: memberTypes,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
});
|
|
const objectConfig = (memberTypes) => ({
|
|
type: 'object',
|
|
properties: {
|
|
memberTypes: {
|
|
oneOf: [arrayConfig(memberTypes), neverConfig],
|
|
},
|
|
order: {
|
|
type: 'string',
|
|
enum: ['alphabetically', 'alphabetically-case-insensitive', 'as-written'],
|
|
},
|
|
},
|
|
additionalProperties: false,
|
|
});
|
|
exports.defaultOrder = [
|
|
// Index signature
|
|
'signature',
|
|
'call-signature',
|
|
// Fields
|
|
'public-static-field',
|
|
'protected-static-field',
|
|
'private-static-field',
|
|
'public-decorated-field',
|
|
'protected-decorated-field',
|
|
'private-decorated-field',
|
|
'public-instance-field',
|
|
'protected-instance-field',
|
|
'private-instance-field',
|
|
'public-abstract-field',
|
|
'protected-abstract-field',
|
|
'private-abstract-field',
|
|
'public-field',
|
|
'protected-field',
|
|
'private-field',
|
|
'static-field',
|
|
'instance-field',
|
|
'abstract-field',
|
|
'decorated-field',
|
|
'field',
|
|
// Static initialization
|
|
'static-initialization',
|
|
// Constructors
|
|
'public-constructor',
|
|
'protected-constructor',
|
|
'private-constructor',
|
|
'constructor',
|
|
// Getters
|
|
'public-static-get',
|
|
'protected-static-get',
|
|
'private-static-get',
|
|
'public-decorated-get',
|
|
'protected-decorated-get',
|
|
'private-decorated-get',
|
|
'public-instance-get',
|
|
'protected-instance-get',
|
|
'private-instance-get',
|
|
'public-abstract-get',
|
|
'protected-abstract-get',
|
|
'private-abstract-get',
|
|
'public-get',
|
|
'protected-get',
|
|
'private-get',
|
|
'static-get',
|
|
'instance-get',
|
|
'abstract-get',
|
|
'decorated-get',
|
|
'get',
|
|
// Setters
|
|
'public-static-set',
|
|
'protected-static-set',
|
|
'private-static-set',
|
|
'public-decorated-set',
|
|
'protected-decorated-set',
|
|
'private-decorated-set',
|
|
'public-instance-set',
|
|
'protected-instance-set',
|
|
'private-instance-set',
|
|
'public-abstract-set',
|
|
'protected-abstract-set',
|
|
'private-abstract-set',
|
|
'public-set',
|
|
'protected-set',
|
|
'private-set',
|
|
'static-set',
|
|
'instance-set',
|
|
'abstract-set',
|
|
'decorated-set',
|
|
'set',
|
|
// Methods
|
|
'public-static-method',
|
|
'protected-static-method',
|
|
'private-static-method',
|
|
'public-decorated-method',
|
|
'protected-decorated-method',
|
|
'private-decorated-method',
|
|
'public-instance-method',
|
|
'protected-instance-method',
|
|
'private-instance-method',
|
|
'public-abstract-method',
|
|
'protected-abstract-method',
|
|
'private-abstract-method',
|
|
'public-method',
|
|
'protected-method',
|
|
'private-method',
|
|
'static-method',
|
|
'instance-method',
|
|
'abstract-method',
|
|
'decorated-method',
|
|
'method',
|
|
];
|
|
const allMemberTypes = Array.from([
|
|
'signature',
|
|
'field',
|
|
'method',
|
|
'call-signature',
|
|
'constructor',
|
|
'get',
|
|
'set',
|
|
'static-initialization',
|
|
].reduce((all, type) => {
|
|
all.add(type);
|
|
['public', 'protected', 'private'].forEach(accessibility => {
|
|
if (type !== 'signature' && type !== 'static-initialization') {
|
|
all.add(`${accessibility}-${type}`); // e.g. `public-field`
|
|
}
|
|
// Only class instance fields, methods, get and set can have decorators attached to them
|
|
if (type === 'field' ||
|
|
type === 'method' ||
|
|
type === 'get' ||
|
|
type === 'set') {
|
|
all.add(`${accessibility}-decorated-${type}`);
|
|
all.add(`decorated-${type}`);
|
|
}
|
|
if (type !== 'constructor' && type !== 'signature') {
|
|
// There is no `static-constructor` or `instance-constructor` or `abstract-constructor`
|
|
['static', 'instance', 'abstract'].forEach(scope => {
|
|
all.add(`${scope}-${type}`);
|
|
all.add(`${accessibility}-${scope}-${type}`);
|
|
});
|
|
}
|
|
});
|
|
return all;
|
|
}, new Set()));
|
|
const functionExpressions = [
|
|
utils_1.AST_NODE_TYPES.FunctionExpression,
|
|
utils_1.AST_NODE_TYPES.ArrowFunctionExpression,
|
|
];
|
|
/**
|
|
* Gets the node type.
|
|
*
|
|
* @param node the node to be evaluated.
|
|
*/
|
|
function getNodeType(node) {
|
|
switch (node.type) {
|
|
case utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition:
|
|
case utils_1.AST_NODE_TYPES.MethodDefinition:
|
|
return node.kind;
|
|
case utils_1.AST_NODE_TYPES.TSMethodSignature:
|
|
return 'method';
|
|
case utils_1.AST_NODE_TYPES.TSCallSignatureDeclaration:
|
|
return 'call-signature';
|
|
case utils_1.AST_NODE_TYPES.TSConstructSignatureDeclaration:
|
|
return 'constructor';
|
|
case utils_1.AST_NODE_TYPES.TSAbstractPropertyDefinition:
|
|
return 'field';
|
|
case utils_1.AST_NODE_TYPES.PropertyDefinition:
|
|
return node.value && functionExpressions.includes(node.value.type)
|
|
? 'method'
|
|
: 'field';
|
|
case utils_1.AST_NODE_TYPES.TSPropertySignature:
|
|
return 'field';
|
|
case utils_1.AST_NODE_TYPES.TSIndexSignature:
|
|
return 'signature';
|
|
case utils_1.AST_NODE_TYPES.StaticBlock:
|
|
return 'static-initialization';
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
/**
|
|
* Gets the raw string value of a member's name
|
|
*/
|
|
function getMemberRawName(member, sourceCode) {
|
|
const { name, type } = util.getNameFromMember(member, sourceCode);
|
|
if (type === util.MemberNameType.Quoted) {
|
|
return name.slice(1, -1);
|
|
}
|
|
if (type === util.MemberNameType.Private) {
|
|
return name.slice(1);
|
|
}
|
|
return name;
|
|
}
|
|
/**
|
|
* Gets the member name based on the member type.
|
|
*
|
|
* @param node the node to be evaluated.
|
|
* @param sourceCode
|
|
*/
|
|
function getMemberName(node, sourceCode) {
|
|
switch (node.type) {
|
|
case utils_1.AST_NODE_TYPES.TSPropertySignature:
|
|
case utils_1.AST_NODE_TYPES.TSMethodSignature:
|
|
case utils_1.AST_NODE_TYPES.TSAbstractPropertyDefinition:
|
|
case utils_1.AST_NODE_TYPES.PropertyDefinition:
|
|
return getMemberRawName(node, sourceCode);
|
|
case utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition:
|
|
case utils_1.AST_NODE_TYPES.MethodDefinition:
|
|
return node.kind === 'constructor'
|
|
? 'constructor'
|
|
: getMemberRawName(node, sourceCode);
|
|
case utils_1.AST_NODE_TYPES.TSConstructSignatureDeclaration:
|
|
return 'new';
|
|
case utils_1.AST_NODE_TYPES.TSCallSignatureDeclaration:
|
|
return 'call';
|
|
case utils_1.AST_NODE_TYPES.TSIndexSignature:
|
|
return util.getNameFromIndexSignature(node);
|
|
case utils_1.AST_NODE_TYPES.StaticBlock:
|
|
return 'static block';
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
/**
|
|
* Gets the calculated rank using the provided method definition.
|
|
* The algorithm is as follows:
|
|
* - Get the rank based on the accessibility-scope-type name, e.g. public-instance-field
|
|
* - If there is no order for accessibility-scope-type, then strip out the accessibility.
|
|
* - If there is no order for scope-type, then strip out the scope.
|
|
* - If there is no order for type, then return -1
|
|
* @param memberGroups the valid names to be validated.
|
|
* @param orderConfig the current order to be validated.
|
|
*
|
|
* @return Index of the matching member type in the order configuration.
|
|
*/
|
|
function getRankOrder(memberGroups, orderConfig) {
|
|
let rank = -1;
|
|
const stack = memberGroups.slice(); // Get a copy of the member groups
|
|
while (stack.length > 0 && rank === -1) {
|
|
const memberGroup = stack.shift();
|
|
rank = orderConfig.findIndex(memberType => Array.isArray(memberType)
|
|
? memberType.includes(memberGroup)
|
|
: memberType === memberGroup);
|
|
}
|
|
return rank;
|
|
}
|
|
/**
|
|
* Gets the rank of the node given the order.
|
|
* @param node the node to be evaluated.
|
|
* @param orderConfig the current order to be validated.
|
|
* @param supportsModifiers a flag indicating whether the type supports modifiers (scope or accessibility) or not.
|
|
*/
|
|
function getRank(node, orderConfig, supportsModifiers) {
|
|
const type = getNodeType(node);
|
|
if (type === null) {
|
|
// shouldn't happen but just in case, put it on the end
|
|
return orderConfig.length - 1;
|
|
}
|
|
const abstract = node.type === utils_1.AST_NODE_TYPES.TSAbstractPropertyDefinition ||
|
|
node.type === utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition;
|
|
const scope = 'static' in node && node.static
|
|
? 'static'
|
|
: abstract
|
|
? 'abstract'
|
|
: 'instance';
|
|
const accessibility = 'accessibility' in node && node.accessibility
|
|
? node.accessibility
|
|
: 'public';
|
|
// Collect all existing member groups that apply to this node...
|
|
// (e.g. 'public-instance-field', 'instance-field', 'public-field', 'constructor' etc.)
|
|
const memberGroups = [];
|
|
if (supportsModifiers) {
|
|
const decorated = 'decorators' in node && node.decorators.length > 0;
|
|
if (decorated &&
|
|
(type === 'field' ||
|
|
type === 'method' ||
|
|
type === 'get' ||
|
|
type === 'set')) {
|
|
memberGroups.push(`${accessibility}-decorated-${type}`);
|
|
memberGroups.push(`decorated-${type}`);
|
|
}
|
|
if (type !== 'signature' && type !== 'static-initialization') {
|
|
if (type !== 'constructor') {
|
|
// Constructors have no scope
|
|
memberGroups.push(`${accessibility}-${scope}-${type}`);
|
|
memberGroups.push(`${scope}-${type}`);
|
|
}
|
|
memberGroups.push(`${accessibility}-${type}`);
|
|
}
|
|
}
|
|
memberGroups.push(type);
|
|
// ...then get the rank order for those member groups based on the node
|
|
return getRankOrder(memberGroups, orderConfig);
|
|
}
|
|
/**
|
|
* Gets the lowest possible rank(s) higher than target.
|
|
* e.g. given the following order:
|
|
* ...
|
|
* public-static-method
|
|
* protected-static-method
|
|
* private-static-method
|
|
* public-instance-method
|
|
* protected-instance-method
|
|
* private-instance-method
|
|
* ...
|
|
* and considering that a public-instance-method has already been declared, so ranks contains
|
|
* public-instance-method, then the lowest possible rank for public-static-method is
|
|
* public-instance-method.
|
|
* If a lowest possible rank is a member group, a comma separated list of ranks is returned.
|
|
* @param ranks the existing ranks in the object.
|
|
* @param target the target rank.
|
|
* @param order the current order to be validated.
|
|
* @returns the name(s) of the lowest possible rank without dashes (-).
|
|
*/
|
|
function getLowestRank(ranks, target, order) {
|
|
let lowest = ranks[ranks.length - 1];
|
|
ranks.forEach(rank => {
|
|
if (rank > target) {
|
|
lowest = Math.min(lowest, rank);
|
|
}
|
|
});
|
|
const lowestRank = order[lowest];
|
|
const lowestRanks = Array.isArray(lowestRank) ? lowestRank : [lowestRank];
|
|
return lowestRanks.map(rank => rank.replace(/-/g, ' ')).join(', ');
|
|
}
|
|
exports.default = util.createRule({
|
|
name: 'member-ordering',
|
|
meta: {
|
|
type: 'suggestion',
|
|
docs: {
|
|
description: 'Require a consistent member declaration order',
|
|
recommended: false,
|
|
},
|
|
messages: {
|
|
incorrectOrder: 'Member {{member}} should be declared before member {{beforeMember}}.',
|
|
incorrectGroupOrder: 'Member {{name}} should be declared before all {{rank}} definitions.',
|
|
},
|
|
schema: [
|
|
{
|
|
type: 'object',
|
|
properties: {
|
|
default: {
|
|
oneOf: [
|
|
neverConfig,
|
|
arrayConfig(allMemberTypes),
|
|
objectConfig(allMemberTypes),
|
|
],
|
|
},
|
|
classes: {
|
|
oneOf: [
|
|
neverConfig,
|
|
arrayConfig(allMemberTypes),
|
|
objectConfig(allMemberTypes),
|
|
],
|
|
},
|
|
classExpressions: {
|
|
oneOf: [
|
|
neverConfig,
|
|
arrayConfig(allMemberTypes),
|
|
objectConfig(allMemberTypes),
|
|
],
|
|
},
|
|
interfaces: {
|
|
oneOf: [
|
|
neverConfig,
|
|
arrayConfig(['signature', 'field', 'method', 'constructor']),
|
|
objectConfig(['signature', 'field', 'method', 'constructor']),
|
|
],
|
|
},
|
|
typeLiterals: {
|
|
oneOf: [
|
|
neverConfig,
|
|
arrayConfig(['signature', 'field', 'method', 'constructor']),
|
|
objectConfig(['signature', 'field', 'method', 'constructor']),
|
|
],
|
|
},
|
|
},
|
|
additionalProperties: false,
|
|
},
|
|
],
|
|
},
|
|
defaultOptions: [
|
|
{
|
|
default: exports.defaultOrder,
|
|
},
|
|
],
|
|
create(context, [options]) {
|
|
/**
|
|
* Checks if the member groups are correctly sorted.
|
|
*
|
|
* @param members Members to be validated.
|
|
* @param groupOrder Group order to be validated.
|
|
* @param supportsModifiers A flag indicating whether the type supports modifiers (scope or accessibility) or not.
|
|
*
|
|
* @return Array of member groups or null if one of the groups is not correctly sorted.
|
|
*/
|
|
function checkGroupSort(members, groupOrder, supportsModifiers) {
|
|
const previousRanks = [];
|
|
const memberGroups = [];
|
|
let isCorrectlySorted = true;
|
|
// Find first member which isn't correctly sorted
|
|
members.forEach(member => {
|
|
const rank = getRank(member, groupOrder, supportsModifiers);
|
|
const name = getMemberName(member, context.getSourceCode());
|
|
const rankLastMember = previousRanks[previousRanks.length - 1];
|
|
if (rank === -1) {
|
|
return;
|
|
}
|
|
// Works for 1st item because x < undefined === false for any x (typeof string)
|
|
if (rank < rankLastMember) {
|
|
context.report({
|
|
node: member,
|
|
messageId: 'incorrectGroupOrder',
|
|
data: {
|
|
name,
|
|
rank: getLowestRank(previousRanks, rank, groupOrder),
|
|
},
|
|
});
|
|
isCorrectlySorted = false;
|
|
}
|
|
else if (rank === rankLastMember) {
|
|
// Same member group --> Push to existing member group array
|
|
memberGroups[memberGroups.length - 1].push(member);
|
|
}
|
|
else {
|
|
// New member group --> Create new member group array
|
|
previousRanks.push(rank);
|
|
memberGroups.push([member]);
|
|
}
|
|
});
|
|
return isCorrectlySorted ? memberGroups : null;
|
|
}
|
|
/**
|
|
* Checks if the members are alphabetically sorted.
|
|
*
|
|
* @param members Members to be validated.
|
|
* @param caseSensitive indicates if the alpha ordering is case sensitive or not.
|
|
*
|
|
* @return True if all members are correctly sorted.
|
|
*/
|
|
function checkAlphaSort(members, caseSensitive) {
|
|
let previousName = '';
|
|
let isCorrectlySorted = true;
|
|
// Find first member which isn't correctly sorted
|
|
members.forEach(member => {
|
|
const name = getMemberName(member, context.getSourceCode());
|
|
// Note: Not all members have names
|
|
if (name) {
|
|
if (caseSensitive
|
|
? name < previousName
|
|
: name.toLowerCase() < previousName.toLowerCase()) {
|
|
context.report({
|
|
node: member,
|
|
messageId: 'incorrectOrder',
|
|
data: {
|
|
member: name,
|
|
beforeMember: previousName,
|
|
},
|
|
});
|
|
isCorrectlySorted = false;
|
|
}
|
|
previousName = name;
|
|
}
|
|
});
|
|
return isCorrectlySorted;
|
|
}
|
|
/**
|
|
* Validates if all members are correctly sorted.
|
|
*
|
|
* @param members Members to be validated.
|
|
* @param orderConfig Order config to be validated.
|
|
* @param supportsModifiers A flag indicating whether the type supports modifiers (scope or accessibility) or not.
|
|
*/
|
|
function validateMembersOrder(members, orderConfig, supportsModifiers) {
|
|
if (orderConfig === 'never') {
|
|
return;
|
|
}
|
|
// Standardize config
|
|
let order = null;
|
|
let memberTypes;
|
|
if (Array.isArray(orderConfig)) {
|
|
memberTypes = orderConfig;
|
|
}
|
|
else {
|
|
order = orderConfig.order;
|
|
memberTypes = orderConfig.memberTypes;
|
|
}
|
|
const hasAlphaSort = order === null || order === void 0 ? void 0 : order.startsWith('alphabetically');
|
|
const alphaSortIsCaseSensitive = order !== 'alphabetically-case-insensitive';
|
|
// Check order
|
|
if (Array.isArray(memberTypes)) {
|
|
const grouped = checkGroupSort(members, memberTypes, supportsModifiers);
|
|
if (grouped === null) {
|
|
return;
|
|
}
|
|
if (hasAlphaSort) {
|
|
grouped.some(groupMember => !checkAlphaSort(groupMember, alphaSortIsCaseSensitive));
|
|
}
|
|
}
|
|
else if (hasAlphaSort) {
|
|
checkAlphaSort(members, alphaSortIsCaseSensitive);
|
|
}
|
|
}
|
|
return {
|
|
ClassDeclaration(node) {
|
|
var _a;
|
|
validateMembersOrder(node.body.body, (_a = options.classes) !== null && _a !== void 0 ? _a : options.default, true);
|
|
},
|
|
ClassExpression(node) {
|
|
var _a;
|
|
validateMembersOrder(node.body.body, (_a = options.classExpressions) !== null && _a !== void 0 ? _a : options.default, true);
|
|
},
|
|
TSInterfaceDeclaration(node) {
|
|
var _a;
|
|
validateMembersOrder(node.body.body, (_a = options.interfaces) !== null && _a !== void 0 ? _a : options.default, false);
|
|
},
|
|
TSTypeLiteral(node) {
|
|
var _a;
|
|
validateMembersOrder(node.members, (_a = options.typeLiterals) !== null && _a !== void 0 ? _a : options.default, false);
|
|
},
|
|
};
|
|
},
|
|
});
|
|
//# sourceMappingURL=member-ordering.js.map |