eslint-plugin-perfectionist
Version:
ESLint plugin for sorting various data such as objects, imports, types, enums, JSX props, etc.
223 lines (222 loc) • 7.27 kB
JavaScript
const commonJsonSchemas = require('../utils/common-json-schemas.js')
const validateCustomSortConfiguration = require('../utils/validate-custom-sort-configuration.js')
const validateGroupsConfiguration = require('../utils/validate-groups-configuration.js')
const getEslintDisabledLines = require('../utils/get-eslint-disabled-lines.js')
const isNodeEslintDisabled = require('../utils/is-node-eslint-disabled.js')
const reportErrors = require('../utils/report-errors.js')
const sortNodesByGroups = require('../utils/sort-nodes-by-groups.js')
const getNodeDecorators = require('../utils/get-node-decorators.js')
const getDecoratorName = require('../utils/get-decorator-name.js')
const createEslintRule = require('../utils/create-eslint-rule.js')
const reportAllErrors = require('../utils/report-all-errors.js')
const shouldPartition = require('../utils/should-partition.js')
const rangeToDiff = require('../utils/range-to-diff.js')
const getSettings = require('../utils/get-settings.js')
const isSortable = require('../utils/is-sortable.js')
const useGroups = require('../utils/use-groups.js')
const complete = require('../utils/complete.js')
let defaultOptions = {
fallbackSort: { type: 'unsorted' },
specialCharacters: 'keep',
partitionByComment: false,
sortOnProperties: true,
sortOnParameters: true,
sortOnAccessors: true,
type: 'alphabetical',
sortOnClasses: true,
sortOnMethods: true,
ignoreCase: true,
customGroups: {},
locales: 'en-US',
alphabet: '',
order: 'asc',
groups: [],
}
const sortDecorators = createEslintRule.createEslintRule({
create: context => {
let settings = getSettings.getSettings(context.settings)
let options = complete.complete(
context.options.at(0),
settings,
defaultOptions,
)
validateCustomSortConfiguration.validateCustomSortConfiguration(options)
validateGroupsConfiguration.validateGroupsConfiguration({
allowedCustomGroups: Object.keys(options.customGroups),
allowedPredefinedGroups: ['unknown'],
options,
})
return {
Decorator: decorator => {
if (!options.sortOnParameters) {
return
}
if (
'decorators' in decorator.parent &&
decorator.parent.type === 'Identifier' &&
decorator.parent.parent.type === 'FunctionExpression'
) {
let { decorators } = decorator.parent
if (decorator !== decorators[0]) {
return
}
sortDecorators$1(context, options, decorators)
}
},
PropertyDefinition: propertyDefinition =>
options.sortOnProperties
? sortDecorators$1(
context,
options,
getNodeDecorators.getNodeDecorators(propertyDefinition),
)
: null,
AccessorProperty: accessorDefinition =>
options.sortOnAccessors
? sortDecorators$1(
context,
options,
getNodeDecorators.getNodeDecorators(accessorDefinition),
)
: null,
MethodDefinition: methodDefinition =>
options.sortOnMethods
? sortDecorators$1(
context,
options,
getNodeDecorators.getNodeDecorators(methodDefinition),
)
: null,
ClassDeclaration: declaration =>
options.sortOnClasses
? sortDecorators$1(
context,
options,
getNodeDecorators.getNodeDecorators(declaration),
)
: null,
}
},
meta: {
schema: [
{
properties: {
...commonJsonSchemas.commonJsonSchemas,
sortOnParameters: {
description:
'Controls whether sorting should be enabled for method parameter decorators.',
type: 'boolean',
},
sortOnProperties: {
description:
'Controls whether sorting should be enabled for class property decorators.',
type: 'boolean',
},
sortOnAccessors: {
description:
'Controls whether sorting should be enabled for class accessor decorators.',
type: 'boolean',
},
sortOnMethods: {
description:
'Controls whether sorting should be enabled for class method decorators.',
type: 'boolean',
},
sortOnClasses: {
description:
'Controls whether sorting should be enabled for class decorators.',
type: 'boolean',
},
partitionByComment: commonJsonSchemas.partitionByCommentJsonSchema,
customGroups: commonJsonSchemas.customGroupsJsonSchema,
groups: commonJsonSchemas.groupsJsonSchema,
},
additionalProperties: false,
type: 'object',
},
],
docs: {
url: 'https://perfectionist.dev/rules/sort-decorators',
description: 'Enforce sorted decorators.',
recommended: true,
},
messages: {
unexpectedDecoratorsGroupOrder: reportErrors.GROUP_ORDER_ERROR,
unexpectedDecoratorsOrder: reportErrors.ORDER_ERROR,
},
type: 'suggestion',
fixable: 'code',
},
defaultOptions: [defaultOptions],
name: 'sort-decorators',
})
let sortDecorators$1 = (context, options, decorators) => {
if (!isSortable.isSortable(decorators)) {
return
}
let { sourceCode, id } = context
let eslintDisabledLines = getEslintDisabledLines.getEslintDisabledLines({
ruleName: id,
sourceCode,
})
let formattedMembers = decorators.reduce(
(accumulator, decorator) => {
var _a
let { setCustomGroups, getGroup } = useGroups.useGroups(options)
let name = getDecoratorName.getDecoratorName({
sourceCode,
decorator,
})
setCustomGroups(options.customGroups, name)
let sortingNode = {
isEslintDisabled: isNodeEslintDisabled.isNodeEslintDisabled(
decorator,
eslintDisabledLines,
),
size: rangeToDiff.rangeToDiff(decorator, sourceCode),
group: getGroup(),
node: decorator,
name,
}
let lastSortingNode =
(_a = accumulator.at(-1)) == null ? void 0 : _a.at(-1)
if (
shouldPartition.shouldPartition({
lastSortingNode,
sortingNode,
sourceCode,
options,
})
) {
accumulator.push([])
}
accumulator.at(-1).push(sortingNode)
return accumulator
},
[[]],
)
let sortNodesExcludingEslintDisabled = ignoreEslintDisabledNodes =>
formattedMembers.flatMap(nodes2 =>
sortNodesByGroups.sortNodesByGroups({
getOptionsByGroupNumber: () => ({ options }),
ignoreEslintDisabledNodes,
groups: options.groups,
nodes: nodes2,
}),
)
let nodes = formattedMembers.flat()
reportAllErrors.reportAllErrors({
availableMessageIds: {
unexpectedGroupOrder: 'unexpectedDecoratorsGroupOrder',
unexpectedOrder: 'unexpectedDecoratorsOrder',
},
ignoreFirstNodeHighestBlockComment: true,
sortNodesExcludingEslintDisabled,
sourceCode,
options,
context,
nodes,
})
}
module.exports = sortDecorators