routie dev init since i didn't adhere to any proper guidance up until now
This commit is contained in:
Generated
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
import { JSONSchema4 } from '@typescript-eslint/utils/json-schema'
|
||||
import { Options } from './sort-array-includes/types.js'
|
||||
declare const ORDER_ERROR_ID = 'unexpectedArrayIncludesOrder'
|
||||
declare const GROUP_ORDER_ERROR_ID = 'unexpectedArrayIncludesGroupOrder'
|
||||
declare const EXTRA_SPACING_ERROR_ID = 'extraSpacingBetweenArrayIncludesMembers'
|
||||
declare const MISSED_SPACING_ERROR_ID =
|
||||
'missedSpacingBetweenArrayIncludesMembers'
|
||||
type MessageId =
|
||||
| typeof MISSED_SPACING_ERROR_ID
|
||||
| typeof EXTRA_SPACING_ERROR_ID
|
||||
| typeof GROUP_ORDER_ERROR_ID
|
||||
| typeof ORDER_ERROR_ID
|
||||
export declare let defaultOptions: Required<Options[number]>
|
||||
export declare let jsonSchema: JSONSchema4
|
||||
declare const _default: import('@typescript-eslint/utils/ts-eslint').RuleModule<
|
||||
MessageId,
|
||||
import('...js').SortArraysOptions,
|
||||
{
|
||||
recommended?: boolean
|
||||
},
|
||||
import('@typescript-eslint/utils/ts-eslint').RuleListener
|
||||
> & {
|
||||
name: string
|
||||
}
|
||||
export default _default
|
||||
Generated
Vendored
+131
@@ -0,0 +1,131 @@
|
||||
import {
|
||||
buildCommonJsonSchemas,
|
||||
buildUseConfigurationIfJsonSchema,
|
||||
matchesAstSelectorJsonSchema,
|
||||
} from '../utils/json-schemas/common-json-schemas.js'
|
||||
import { buildCommonGroupsJsonSchemas } from '../utils/json-schemas/common-groups-json-schemas.js'
|
||||
import {
|
||||
EXTRA_SPACING_ERROR,
|
||||
GROUP_ORDER_ERROR,
|
||||
MISSED_SPACING_ERROR,
|
||||
ORDER_ERROR,
|
||||
} from '../utils/report-errors.js'
|
||||
import {
|
||||
partitionByCommentJsonSchema,
|
||||
partitionByNewLineJsonSchema,
|
||||
} from '../utils/json-schemas/common-partition-json-schemas.js'
|
||||
import { buildAstListeners } from '../utils/build-ast-listeners.js'
|
||||
import { createEslintRule } from '../utils/create-eslint-rule.js'
|
||||
import { additionalCustomGroupMatchOptionsJsonSchema } from './sort-arrays/types.js'
|
||||
import { sortArray } from './sort-arrays/sort-array.js'
|
||||
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
|
||||
/**
|
||||
* Cache computed groups by modifiers and selectors for performance.
|
||||
*/
|
||||
var cachedGroupsByModifiersAndSelectors = /* @__PURE__ */ new Map()
|
||||
var ORDER_ERROR_ID = 'unexpectedArrayIncludesOrder'
|
||||
var GROUP_ORDER_ERROR_ID = 'unexpectedArrayIncludesGroupOrder'
|
||||
var EXTRA_SPACING_ERROR_ID = 'extraSpacingBetweenArrayIncludesMembers'
|
||||
var MISSED_SPACING_ERROR_ID = 'missedSpacingBetweenArrayIncludesMembers'
|
||||
var defaultOptions = {
|
||||
fallbackSort: { type: 'unsorted' },
|
||||
newlinesInside: 'newlinesBetween',
|
||||
specialCharacters: 'keep',
|
||||
partitionByComment: false,
|
||||
partitionByNewLine: false,
|
||||
newlinesBetween: 'ignore',
|
||||
useConfigurationIf: {},
|
||||
type: 'alphabetical',
|
||||
groups: ['literal'],
|
||||
ignoreCase: true,
|
||||
locales: 'en-US',
|
||||
customGroups: [],
|
||||
alphabet: '',
|
||||
order: 'asc',
|
||||
}
|
||||
var jsonSchema = {
|
||||
items: {
|
||||
properties: {
|
||||
...buildCommonJsonSchemas(),
|
||||
...buildCommonGroupsJsonSchemas({
|
||||
additionalCustomGroupMatchProperties:
|
||||
additionalCustomGroupMatchOptionsJsonSchema,
|
||||
}),
|
||||
useConfigurationIf: buildUseConfigurationIfJsonSchema({
|
||||
additionalProperties: {
|
||||
matchesAstSelector: matchesAstSelectorJsonSchema,
|
||||
},
|
||||
}),
|
||||
partitionByComment: partitionByCommentJsonSchema,
|
||||
partitionByNewLine: partitionByNewLineJsonSchema,
|
||||
},
|
||||
additionalProperties: false,
|
||||
type: 'object',
|
||||
},
|
||||
uniqueItems: true,
|
||||
type: 'array',
|
||||
}
|
||||
var sort_array_includes_default = createEslintRule({
|
||||
meta: {
|
||||
messages: {
|
||||
[MISSED_SPACING_ERROR_ID]: MISSED_SPACING_ERROR,
|
||||
[EXTRA_SPACING_ERROR_ID]: EXTRA_SPACING_ERROR,
|
||||
[GROUP_ORDER_ERROR_ID]: GROUP_ORDER_ERROR,
|
||||
[ORDER_ERROR_ID]: ORDER_ERROR,
|
||||
},
|
||||
docs: {
|
||||
description: 'Enforce sorted arrays before include method.',
|
||||
url: 'https://perfectionist.dev/rules/sort-array-includes',
|
||||
recommended: true,
|
||||
},
|
||||
schema: jsonSchema,
|
||||
type: 'suggestion',
|
||||
fixable: 'code',
|
||||
},
|
||||
create: context =>
|
||||
buildAstListeners({
|
||||
nodeTypes: [AST_NODE_TYPES.NewExpression, AST_NODE_TYPES.ArrayExpression],
|
||||
sorter: sortPotentiallyValidArray,
|
||||
context,
|
||||
}),
|
||||
defaultOptions: [defaultOptions],
|
||||
name: 'sort-array-includes',
|
||||
})
|
||||
function sortPotentiallyValidArray({ matchedAstSelectors, context, node }) {
|
||||
if (!isValidArray()) {
|
||||
return
|
||||
}
|
||||
sortArray({
|
||||
availableMessageIds: {
|
||||
missedSpacingBetweenMembers: MISSED_SPACING_ERROR_ID,
|
||||
extraSpacingBetweenMembers: EXTRA_SPACING_ERROR_ID,
|
||||
unexpectedGroupOrder: GROUP_ORDER_ERROR_ID,
|
||||
unexpectedOrder: ORDER_ERROR_ID,
|
||||
},
|
||||
mustHaveMatchedContextOptions: false,
|
||||
cachedGroupsByModifiersAndSelectors,
|
||||
matchedAstSelectors,
|
||||
defaultOptions,
|
||||
context,
|
||||
node,
|
||||
})
|
||||
function isValidArray() {
|
||||
if (node.parent.type !== AST_NODE_TYPES.MemberExpression) {
|
||||
return false
|
||||
}
|
||||
if (node.parent.property.type !== AST_NODE_TYPES.Identifier) {
|
||||
return false
|
||||
}
|
||||
if (node.parent.property.name !== 'includes') {
|
||||
return false
|
||||
}
|
||||
if (node.parent.parent.type !== AST_NODE_TYPES.CallExpression) {
|
||||
return false
|
||||
}
|
||||
if (node.parent.parent.callee !== node.parent) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
export { sort_array_includes_default as default, defaultOptions, jsonSchema }
|
||||
Generated
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
import { Options as SortArraysOptions } from '../sort-arrays/types.js'
|
||||
export type Options = SortArraysOptions
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import { Options } from './sort-arrays/types.js'
|
||||
declare const ORDER_ERROR_ID = 'unexpectedArraysOrder'
|
||||
declare const GROUP_ORDER_ERROR_ID = 'unexpectedArraysGroupOrder'
|
||||
declare const EXTRA_SPACING_ERROR_ID = 'extraSpacingBetweenArraysMembers'
|
||||
declare const MISSED_SPACING_ERROR_ID = 'missedSpacingBetweenArraysMembers'
|
||||
type MessageId =
|
||||
| typeof MISSED_SPACING_ERROR_ID
|
||||
| typeof EXTRA_SPACING_ERROR_ID
|
||||
| typeof GROUP_ORDER_ERROR_ID
|
||||
| typeof ORDER_ERROR_ID
|
||||
declare const _default: import('@typescript-eslint/utils/ts-eslint').RuleModule<
|
||||
MessageId,
|
||||
Options,
|
||||
{
|
||||
recommended?: boolean
|
||||
},
|
||||
import('@typescript-eslint/utils/ts-eslint').RuleListener
|
||||
> & {
|
||||
name: string
|
||||
}
|
||||
export default _default
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
import {
|
||||
buildCommonJsonSchemas,
|
||||
buildUseConfigurationIfJsonSchema,
|
||||
matchesAstSelectorJsonSchema,
|
||||
} from '../utils/json-schemas/common-json-schemas.js'
|
||||
import { buildCommonGroupsJsonSchemas } from '../utils/json-schemas/common-groups-json-schemas.js'
|
||||
import {
|
||||
EXTRA_SPACING_ERROR,
|
||||
GROUP_ORDER_ERROR,
|
||||
MISSED_SPACING_ERROR,
|
||||
ORDER_ERROR,
|
||||
} from '../utils/report-errors.js'
|
||||
import {
|
||||
partitionByCommentJsonSchema,
|
||||
partitionByNewLineJsonSchema,
|
||||
} from '../utils/json-schemas/common-partition-json-schemas.js'
|
||||
import { buildAstListeners } from '../utils/build-ast-listeners.js'
|
||||
import { createEslintRule } from '../utils/create-eslint-rule.js'
|
||||
import { additionalCustomGroupMatchOptionsJsonSchema } from './sort-arrays/types.js'
|
||||
import { sortArray } from './sort-arrays/sort-array.js'
|
||||
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
|
||||
/**
|
||||
* Cache computed groups by modifiers and selectors for performance.
|
||||
*/
|
||||
var cachedGroupsByModifiersAndSelectors = /* @__PURE__ */ new Map()
|
||||
var ORDER_ERROR_ID = 'unexpectedArraysOrder'
|
||||
var GROUP_ORDER_ERROR_ID = 'unexpectedArraysGroupOrder'
|
||||
var EXTRA_SPACING_ERROR_ID = 'extraSpacingBetweenArraysMembers'
|
||||
var MISSED_SPACING_ERROR_ID = 'missedSpacingBetweenArraysMembers'
|
||||
var defaultOptions = {
|
||||
fallbackSort: { type: 'unsorted' },
|
||||
newlinesInside: 'newlinesBetween',
|
||||
specialCharacters: 'keep',
|
||||
partitionByComment: false,
|
||||
partitionByNewLine: false,
|
||||
newlinesBetween: 'ignore',
|
||||
useConfigurationIf: {},
|
||||
type: 'alphabetical',
|
||||
groups: ['literal'],
|
||||
ignoreCase: true,
|
||||
locales: 'en-US',
|
||||
customGroups: [],
|
||||
alphabet: '',
|
||||
order: 'asc',
|
||||
}
|
||||
var jsonSchema = {
|
||||
items: {
|
||||
properties: {
|
||||
...buildCommonJsonSchemas(),
|
||||
...buildCommonGroupsJsonSchemas({
|
||||
additionalCustomGroupMatchProperties:
|
||||
additionalCustomGroupMatchOptionsJsonSchema,
|
||||
}),
|
||||
useConfigurationIf: buildUseConfigurationIfJsonSchema({
|
||||
additionalProperties: {
|
||||
matchesAstSelector: matchesAstSelectorJsonSchema,
|
||||
},
|
||||
}),
|
||||
partitionByComment: partitionByCommentJsonSchema,
|
||||
partitionByNewLine: partitionByNewLineJsonSchema,
|
||||
},
|
||||
required: ['useConfigurationIf'],
|
||||
additionalProperties: false,
|
||||
type: 'object',
|
||||
},
|
||||
uniqueItems: true,
|
||||
type: 'array',
|
||||
}
|
||||
var sort_arrays_default = createEslintRule({
|
||||
meta: {
|
||||
messages: {
|
||||
[MISSED_SPACING_ERROR_ID]: MISSED_SPACING_ERROR,
|
||||
[EXTRA_SPACING_ERROR_ID]: EXTRA_SPACING_ERROR,
|
||||
[GROUP_ORDER_ERROR_ID]: GROUP_ORDER_ERROR,
|
||||
[ORDER_ERROR_ID]: ORDER_ERROR,
|
||||
},
|
||||
docs: {
|
||||
url: 'https://perfectionist.dev/rules/sort-arrays',
|
||||
description: 'Enforce sorted arrays.',
|
||||
recommended: false,
|
||||
},
|
||||
schema: jsonSchema,
|
||||
type: 'suggestion',
|
||||
fixable: 'code',
|
||||
},
|
||||
create: context =>
|
||||
buildAstListeners({
|
||||
nodeTypes: [AST_NODE_TYPES.NewExpression, AST_NODE_TYPES.ArrayExpression],
|
||||
context,
|
||||
sorter,
|
||||
}),
|
||||
defaultOptions: [defaultOptions],
|
||||
name: 'sort-arrays',
|
||||
})
|
||||
function sorter({ matchedAstSelectors, context, node }) {
|
||||
sortArray({
|
||||
availableMessageIds: {
|
||||
missedSpacingBetweenMembers: MISSED_SPACING_ERROR_ID,
|
||||
extraSpacingBetweenMembers: EXTRA_SPACING_ERROR_ID,
|
||||
unexpectedGroupOrder: GROUP_ORDER_ERROR_ID,
|
||||
unexpectedOrder: ORDER_ERROR_ID,
|
||||
},
|
||||
cachedGroupsByModifiersAndSelectors,
|
||||
mustHaveMatchedContextOptions: true,
|
||||
matchedAstSelectors,
|
||||
defaultOptions,
|
||||
context,
|
||||
node,
|
||||
})
|
||||
}
|
||||
export { sort_arrays_default as default }
|
||||
frontend/node_modules/eslint-plugin-perfectionist/dist/rules/sort-arrays/compute-array-elements.d.ts
Generated
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
import { TSESTree } from '@typescript-eslint/types'
|
||||
/**
|
||||
* Computes array elements for the given expression.
|
||||
*
|
||||
* @param expression - The expression to compute array elements from.
|
||||
* @returns An array of elements if the expression is an array or a new
|
||||
* expression, otherwise null.
|
||||
*/
|
||||
export declare function computeArrayElements(
|
||||
expression: TSESTree.ArrayExpression | TSESTree.NewExpression,
|
||||
): (TSESTree.SpreadElement | TSESTree.Expression | null)[] | null
|
||||
Generated
Vendored
+27
@@ -0,0 +1,27 @@
|
||||
import { UnreachableCaseError } from '../../utils/unreachable-case-error.js'
|
||||
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
|
||||
/**
|
||||
* Computes array elements for the given expression.
|
||||
*
|
||||
* @param expression - The expression to compute array elements from.
|
||||
* @returns An array of elements if the expression is an array or a new
|
||||
* expression, otherwise null.
|
||||
*/
|
||||
function computeArrayElements(expression) {
|
||||
switch (expression.type) {
|
||||
case AST_NODE_TYPES.ArrayExpression:
|
||||
return expression.elements
|
||||
case AST_NODE_TYPES.NewExpression:
|
||||
if (expression.callee.type !== AST_NODE_TYPES.Identifier) {
|
||||
return null
|
||||
}
|
||||
if (expression.callee.name !== 'Array') {
|
||||
return null
|
||||
}
|
||||
return expression.arguments
|
||||
/* v8 ignore next 2 -- @preserve Exhaustive guard. */
|
||||
default:
|
||||
throw new UnreachableCaseError(expression)
|
||||
}
|
||||
}
|
||||
export { computeArrayElements }
|
||||
Generated
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
import { RuleContext } from '@typescript-eslint/utils/ts-eslint'
|
||||
import { TSESTree } from '@typescript-eslint/types'
|
||||
import { Options } from './types.js'
|
||||
/**
|
||||
* Computes the matched context options for a given array node.
|
||||
*
|
||||
* @param params - Parameters.
|
||||
* @param params.matchedAstSelectors - The matched AST selectors for an array
|
||||
* node.
|
||||
* @param params.elements - The array elements to compute the context options
|
||||
* for.
|
||||
* @param params.context - The rule context.
|
||||
* @returns The matched context options or undefined if none match.
|
||||
*/
|
||||
export declare function computeMatchedContextOptions<
|
||||
MessageIds extends string,
|
||||
>({
|
||||
matchedAstSelectors,
|
||||
elements,
|
||||
context,
|
||||
}: {
|
||||
elements: (TSESTree.SpreadElement | TSESTree.Expression | null)[]
|
||||
context: Readonly<RuleContext<MessageIds, Options>>
|
||||
matchedAstSelectors: ReadonlySet<string>
|
||||
}): Options[number] | undefined
|
||||
Generated
Vendored
+51
@@ -0,0 +1,51 @@
|
||||
import { passesAllNamesMatchPatternFilter } from '../../utils/context-matching/passes-all-names-match-pattern-filter.js'
|
||||
import { passesAstSelectorFilter } from '../../utils/context-matching/passes-ast-selector-filter.js'
|
||||
import { computeNodeName } from './compute-node-name.js'
|
||||
/**
|
||||
* Computes the matched context options for a given array node.
|
||||
*
|
||||
* @param params - Parameters.
|
||||
* @param params.matchedAstSelectors - The matched AST selectors for an array
|
||||
* node.
|
||||
* @param params.elements - The array elements to compute the context options
|
||||
* for.
|
||||
* @param params.context - The rule context.
|
||||
* @returns The matched context options or undefined if none match.
|
||||
*/
|
||||
function computeMatchedContextOptions({
|
||||
matchedAstSelectors,
|
||||
elements,
|
||||
context,
|
||||
}) {
|
||||
let nodeNames = elements
|
||||
.filter(element => element !== null)
|
||||
.map(element =>
|
||||
computeNodeName({
|
||||
sourceCode: context.sourceCode,
|
||||
node: element,
|
||||
}),
|
||||
)
|
||||
return context.options.find(options =>
|
||||
isContextOptionMatching({
|
||||
matchedAstSelectors,
|
||||
nodeNames,
|
||||
options,
|
||||
}),
|
||||
)
|
||||
}
|
||||
function isContextOptionMatching({ matchedAstSelectors, nodeNames, options }) {
|
||||
if (!options.useConfigurationIf) {
|
||||
return true
|
||||
}
|
||||
return (
|
||||
passesAllNamesMatchPatternFilter({
|
||||
allNamesMatchPattern: options.useConfigurationIf.allNamesMatchPattern,
|
||||
nodeNames,
|
||||
}) &&
|
||||
passesAstSelectorFilter({
|
||||
matchesAstSelector: options.useConfigurationIf.matchesAstSelector,
|
||||
matchedAstSelectors,
|
||||
})
|
||||
)
|
||||
}
|
||||
export { computeMatchedContextOptions }
|
||||
Generated
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
import { TSESTree } from '@typescript-eslint/types'
|
||||
import { TSESLint } from '@typescript-eslint/utils'
|
||||
/**
|
||||
* Computes the name of an array member.
|
||||
*
|
||||
* @param props - The parameters object.
|
||||
* @param props.sourceCode - ESLint source code object for text extraction.
|
||||
* @param props.node - The AST node representing an array member.
|
||||
* @returns The name of the array member.
|
||||
*/
|
||||
export declare function computeNodeName({
|
||||
sourceCode,
|
||||
node,
|
||||
}: {
|
||||
node: TSESTree.SpreadElement | TSESTree.Expression
|
||||
sourceCode: TSESLint.SourceCode
|
||||
}): string
|
||||
Generated
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
|
||||
/**
|
||||
* Computes the name of an array member.
|
||||
*
|
||||
* @param props - The parameters object.
|
||||
* @param props.sourceCode - ESLint source code object for text extraction.
|
||||
* @param props.node - The AST node representing an array member.
|
||||
* @returns The name of the array member.
|
||||
*/
|
||||
function computeNodeName({ sourceCode, node }) {
|
||||
return node.type === AST_NODE_TYPES.Literal ?
|
||||
`${node.value}`
|
||||
: sourceCode.getText(node)
|
||||
}
|
||||
export { computeNodeName }
|
||||
Generated
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
import { RuleContext } from '@typescript-eslint/utils/ts-eslint'
|
||||
import { TSESTree } from '@typescript-eslint/types'
|
||||
import { Options } from './types.js'
|
||||
export declare function sortArray<MessageIds extends string>({
|
||||
cachedGroupsByModifiersAndSelectors,
|
||||
mustHaveMatchedContextOptions,
|
||||
availableMessageIds,
|
||||
matchedAstSelectors,
|
||||
defaultOptions,
|
||||
context,
|
||||
node,
|
||||
}: {
|
||||
availableMessageIds: {
|
||||
missedSpacingBetweenMembers: MessageIds
|
||||
extraSpacingBetweenMembers: MessageIds
|
||||
unexpectedGroupOrder: MessageIds
|
||||
unexpectedOrder: MessageIds
|
||||
}
|
||||
cachedGroupsByModifiersAndSelectors: Map<string, string[]>
|
||||
node: TSESTree.ArrayExpression | TSESTree.NewExpression
|
||||
context: Readonly<RuleContext<MessageIds, Options>>
|
||||
defaultOptions: Required<Options[number]>
|
||||
matchedAstSelectors: ReadonlySet<string>
|
||||
mustHaveMatchedContextOptions: boolean
|
||||
}): void
|
||||
Generated
Vendored
+136
@@ -0,0 +1,136 @@
|
||||
import { validateNewlinesAndPartitionConfiguration } from '../../utils/validate-newlines-and-partition-configuration.js'
|
||||
import { defaultComparatorByOptionsComputer } from '../../utils/compare/default-comparator-by-options-computer.js'
|
||||
import { buildOptionsByGroupIndexComputer } from '../../utils/build-options-by-group-index-computer.js'
|
||||
import { validateCustomSortConfiguration } from '../../utils/validate-custom-sort-configuration.js'
|
||||
import { validateGroupsConfiguration } from '../../utils/validate-groups-configuration.js'
|
||||
import { generatePredefinedGroups } from '../../utils/generate-predefined-groups.js'
|
||||
import { getEslintDisabledLines } from '../../utils/get-eslint-disabled-lines.js'
|
||||
import { doesCustomGroupMatch } from '../../utils/does-custom-group-match.js'
|
||||
import { isNodeEslintDisabled } from '../../utils/is-node-eslint-disabled.js'
|
||||
import { sortNodesByGroups } from '../../utils/sort-nodes-by-groups.js'
|
||||
import { reportAllErrors } from '../../utils/report-all-errors.js'
|
||||
import { shouldPartition } from '../../utils/should-partition.js'
|
||||
import { computeGroup } from '../../utils/compute-group.js'
|
||||
import { rangeToDiff } from '../../utils/range-to-diff.js'
|
||||
import { getSettings } from '../../utils/get-settings.js'
|
||||
import { isSortable } from '../../utils/is-sortable.js'
|
||||
import { complete } from '../../utils/complete.js'
|
||||
import { allSelectors } from './types.js'
|
||||
import { computeNodeName } from './compute-node-name.js'
|
||||
import { computeMatchedContextOptions } from './compute-matched-context-options.js'
|
||||
import { computeArrayElements } from './compute-array-elements.js'
|
||||
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
|
||||
function sortArray({
|
||||
cachedGroupsByModifiersAndSelectors,
|
||||
mustHaveMatchedContextOptions,
|
||||
availableMessageIds,
|
||||
matchedAstSelectors,
|
||||
defaultOptions,
|
||||
context,
|
||||
node,
|
||||
}) {
|
||||
let elements = computeArrayElements(node)
|
||||
if (!elements) {
|
||||
return
|
||||
}
|
||||
if (!isSortable(elements)) {
|
||||
return
|
||||
}
|
||||
let { sourceCode, id } = context
|
||||
let settings = getSettings(context.settings)
|
||||
let matchedContextOptions = computeMatchedContextOptions({
|
||||
matchedAstSelectors,
|
||||
elements,
|
||||
context,
|
||||
})
|
||||
if (mustHaveMatchedContextOptions && !matchedContextOptions) {
|
||||
return
|
||||
}
|
||||
let options = complete(matchedContextOptions, settings, defaultOptions)
|
||||
validateCustomSortConfiguration(options)
|
||||
validateGroupsConfiguration({
|
||||
selectors: allSelectors,
|
||||
modifiers: [],
|
||||
options,
|
||||
})
|
||||
validateNewlinesAndPartitionConfiguration(options)
|
||||
let eslintDisabledLines = getEslintDisabledLines({
|
||||
ruleName: id,
|
||||
sourceCode,
|
||||
})
|
||||
let optionsByGroupIndexComputer = buildOptionsByGroupIndexComputer(options)
|
||||
let formattedMembers = elements.reduce(
|
||||
(accumulator, element) => {
|
||||
if (element === null) {
|
||||
return accumulator
|
||||
}
|
||||
if (element.type === AST_NODE_TYPES.SpreadElement) {
|
||||
accumulator.push([])
|
||||
return accumulator
|
||||
}
|
||||
let name = computeNodeName({
|
||||
node: element,
|
||||
sourceCode,
|
||||
})
|
||||
let selector = 'literal'
|
||||
let group = computeGroup({
|
||||
customGroupMatcher: customGroup =>
|
||||
doesCustomGroupMatch({
|
||||
selectors: [selector],
|
||||
elementName: name,
|
||||
modifiers: [],
|
||||
customGroup,
|
||||
}),
|
||||
predefinedGroups: generatePredefinedGroups({
|
||||
cache: cachedGroupsByModifiersAndSelectors,
|
||||
selectors: [selector],
|
||||
modifiers: [],
|
||||
}),
|
||||
options,
|
||||
})
|
||||
let sortingNode = {
|
||||
isEslintDisabled: isNodeEslintDisabled(element, eslintDisabledLines),
|
||||
size: rangeToDiff(element, sourceCode),
|
||||
node: element,
|
||||
group,
|
||||
name,
|
||||
}
|
||||
let lastSortingNode = accumulator.at(-1)?.at(-1)
|
||||
if (
|
||||
shouldPartition({
|
||||
lastSortingNode,
|
||||
sortingNode,
|
||||
sourceCode,
|
||||
options,
|
||||
})
|
||||
) {
|
||||
accumulator.push([])
|
||||
}
|
||||
accumulator.at(-1).push({
|
||||
...sortingNode,
|
||||
partitionId: accumulator.length,
|
||||
})
|
||||
return accumulator
|
||||
},
|
||||
[[]],
|
||||
)
|
||||
function sortNodesExcludingEslintDisabled(ignoreEslintDisabledNodes) {
|
||||
return formattedMembers.flatMap(nodes =>
|
||||
sortNodesByGroups({
|
||||
comparatorByOptionsComputer: defaultComparatorByOptionsComputer,
|
||||
optionsByGroupIndexComputer,
|
||||
ignoreEslintDisabledNodes,
|
||||
groups: options.groups,
|
||||
nodes,
|
||||
}),
|
||||
)
|
||||
}
|
||||
reportAllErrors({
|
||||
sortNodesExcludingEslintDisabled,
|
||||
availableMessageIds,
|
||||
options,
|
||||
context,
|
||||
nodes: formattedMembers.flat(),
|
||||
})
|
||||
}
|
||||
export { sortArray }
|
||||
Generated
Vendored
+74
@@ -0,0 +1,74 @@
|
||||
import { JSONSchema4 } from '@typescript-eslint/utils/json-schema'
|
||||
import { RegexOption, TypeOption } from '../../types/common-options.js'
|
||||
import { AllCommonOptions } from '../../types/all-common-options.js'
|
||||
/**
|
||||
* Configuration options for the sort-array-includes rule.
|
||||
*
|
||||
* This rule enforces the sorting of arrays passed to the `.includes()` method,
|
||||
* ensuring consistent ordering of array elements for better readability and
|
||||
* maintainability.
|
||||
*/
|
||||
export type Options = Partial<
|
||||
{
|
||||
/**
|
||||
* Conditional configuration based on pattern matching.
|
||||
*/
|
||||
useConfigurationIf: {
|
||||
/**
|
||||
* Regular expression pattern to match against all array element names.
|
||||
* The rule is only applied when all names match this pattern.
|
||||
*/
|
||||
allNamesMatchPattern?: RegexOption
|
||||
/**
|
||||
* AST selector to match against ArrayExpression nodes.
|
||||
*/
|
||||
matchesAstSelector?: string
|
||||
}
|
||||
} & AllCommonOptions<
|
||||
TypeOption,
|
||||
AdditionalSortOptions,
|
||||
CustomGroupMatchOptions
|
||||
>
|
||||
>[]
|
||||
/**
|
||||
* Represents the type of array element selector. Note: Spread elements are not
|
||||
* sorted and act as partition boundaries.
|
||||
*/
|
||||
export type Selector = (typeof allSelectors)[number]
|
||||
/**
|
||||
* Additional configuration for a single custom group.
|
||||
*
|
||||
* Custom groups allow fine-grained control over how array elements are grouped
|
||||
* and sorted based on their names and types.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```ts
|
||||
* {
|
||||
* "selector": "literal"
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
interface CustomGroupMatchOptions {
|
||||
/**
|
||||
* Specifies the type of array elements to include in this group. Only
|
||||
* 'literal' is available since spread elements create partition boundaries
|
||||
* and are not sorted.
|
||||
*/
|
||||
selector?: Selector
|
||||
}
|
||||
type AdditionalSortOptions = object
|
||||
/**
|
||||
* Complete list of available selectors for array elements. Used for validation
|
||||
* and JSON schema generation.
|
||||
*/
|
||||
export declare let allSelectors: readonly ['literal']
|
||||
/**
|
||||
* Additional custom group match options JSON schema. Used by ESLint to validate
|
||||
* rule options at configuration time.
|
||||
*/
|
||||
export declare let additionalCustomGroupMatchOptionsJsonSchema: Record<
|
||||
string,
|
||||
JSONSchema4
|
||||
>
|
||||
export {}
|
||||
Generated
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
import { buildCustomGroupSelectorJsonSchema } from '../../utils/json-schemas/common-groups-json-schemas.js'
|
||||
/**
|
||||
* Complete list of available selectors for array elements. Used for validation
|
||||
* and JSON schema generation.
|
||||
*/
|
||||
var allSelectors = ['literal']
|
||||
/**
|
||||
* Additional custom group match options JSON schema. Used by ESLint to validate
|
||||
* rule options at configuration time.
|
||||
*/
|
||||
var additionalCustomGroupMatchOptionsJsonSchema = {
|
||||
selector: buildCustomGroupSelectorJsonSchema(allSelectors),
|
||||
}
|
||||
export { additionalCustomGroupMatchOptionsJsonSchema, allSelectors }
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { MessageId, Options } from './sort-classes/types.js'
|
||||
declare const _default: import('@typescript-eslint/utils/ts-eslint').RuleModule<
|
||||
MessageId,
|
||||
Options,
|
||||
{
|
||||
recommended?: boolean
|
||||
},
|
||||
import('@typescript-eslint/utils/ts-eslint').RuleListener
|
||||
> & {
|
||||
name: string
|
||||
}
|
||||
export default _default
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
import {
|
||||
buildCommonJsonSchemas,
|
||||
buildRegexJsonSchema,
|
||||
buildUseConfigurationIfJsonSchema,
|
||||
matchesAstSelectorJsonSchema,
|
||||
useExperimentalDependencyDetectionJsonSchema,
|
||||
} from '../utils/json-schemas/common-json-schemas.js'
|
||||
import {
|
||||
buildCommonGroupsJsonSchemas,
|
||||
newlinesBetweenJsonSchema,
|
||||
} from '../utils/json-schemas/common-groups-json-schemas.js'
|
||||
import {
|
||||
DEPENDENCY_ORDER_ERROR,
|
||||
EXTRA_SPACING_ERROR,
|
||||
GROUP_ORDER_ERROR,
|
||||
MISSED_SPACING_ERROR,
|
||||
ORDER_ERROR,
|
||||
} from '../utils/report-errors.js'
|
||||
import {
|
||||
partitionByCommentJsonSchema,
|
||||
partitionByNewLineJsonSchema,
|
||||
} from '../utils/json-schemas/common-partition-json-schemas.js'
|
||||
import { buildAstListeners } from '../utils/build-ast-listeners.js'
|
||||
import { createEslintRule } from '../utils/create-eslint-rule.js'
|
||||
import {
|
||||
DEPENDENCY_ORDER_ERROR_ID,
|
||||
EXTRA_SPACING_ERROR_ID,
|
||||
GROUP_ORDER_ERROR_ID,
|
||||
MISSED_SPACING_ERROR_ID,
|
||||
ORDER_ERROR_ID,
|
||||
additionalCustomGroupMatchOptionsJsonSchema,
|
||||
} from './sort-classes/types.js'
|
||||
import { defaultOptions, sortClass } from './sort-classes/sort-class.js'
|
||||
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
|
||||
var sort_classes_default = createEslintRule({
|
||||
meta: {
|
||||
schema: {
|
||||
items: {
|
||||
properties: {
|
||||
...buildCommonJsonSchemas(),
|
||||
...buildCommonGroupsJsonSchemas({
|
||||
additionalCustomGroupMatchProperties:
|
||||
additionalCustomGroupMatchOptionsJsonSchema,
|
||||
}),
|
||||
useConfigurationIf: buildUseConfigurationIfJsonSchema({
|
||||
additionalProperties: {
|
||||
matchesAstSelector: matchesAstSelectorJsonSchema,
|
||||
},
|
||||
}),
|
||||
useExperimentalDependencyDetection:
|
||||
useExperimentalDependencyDetectionJsonSchema,
|
||||
newlinesBetweenOverloadSignatures: newlinesBetweenJsonSchema,
|
||||
ignoreCallbackDependenciesPatterns: buildRegexJsonSchema(),
|
||||
partitionByComment: partitionByCommentJsonSchema,
|
||||
partitionByNewLine: partitionByNewLineJsonSchema,
|
||||
},
|
||||
additionalProperties: false,
|
||||
type: 'object',
|
||||
},
|
||||
uniqueItems: true,
|
||||
type: 'array',
|
||||
},
|
||||
messages: {
|
||||
[DEPENDENCY_ORDER_ERROR_ID]: DEPENDENCY_ORDER_ERROR,
|
||||
[MISSED_SPACING_ERROR_ID]: MISSED_SPACING_ERROR,
|
||||
[EXTRA_SPACING_ERROR_ID]: EXTRA_SPACING_ERROR,
|
||||
[GROUP_ORDER_ERROR_ID]: GROUP_ORDER_ERROR,
|
||||
[ORDER_ERROR_ID]: ORDER_ERROR,
|
||||
},
|
||||
docs: {
|
||||
url: 'https://perfectionist.dev/rules/sort-classes',
|
||||
description: 'Enforce sorted classes.',
|
||||
recommended: true,
|
||||
},
|
||||
type: 'suggestion',
|
||||
fixable: 'code',
|
||||
},
|
||||
create: context =>
|
||||
buildAstListeners({
|
||||
nodeTypes: [AST_NODE_TYPES.ClassBody],
|
||||
sorter: sortClass,
|
||||
context,
|
||||
}),
|
||||
defaultOptions: [defaultOptions],
|
||||
name: 'sort-classes',
|
||||
})
|
||||
export { sort_classes_default as default }
|
||||
Generated
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
import { TSESTree } from '@typescript-eslint/types'
|
||||
import { TSESLint } from '@typescript-eslint/utils'
|
||||
import { SortClassesSortingNode } from './types.js'
|
||||
import { RegexOption } from '../../types/common-options.js'
|
||||
type SortingNodeWithoutDependencies = Omit<
|
||||
SortClassesSortingNode,
|
||||
'dependencies'
|
||||
>
|
||||
export declare function computeDependenciesBySortingNode({
|
||||
ignoreCallbackDependenciesPatterns,
|
||||
sortingNodes,
|
||||
sourceCode,
|
||||
classBody,
|
||||
}: {
|
||||
ignoreCallbackDependenciesPatterns: RegexOption
|
||||
sortingNodes: SortingNodeWithoutDependencies[]
|
||||
sourceCode: TSESLint.SourceCode
|
||||
classBody: TSESTree.ClassBody
|
||||
}): Map<SortingNodeWithoutDependencies, SortingNodeWithoutDependencies[]>
|
||||
export {}
|
||||
Generated
Vendored
+186
@@ -0,0 +1,186 @@
|
||||
import { UnreachableCaseError } from '../../utils/unreachable-case-error.js'
|
||||
import { matches } from '../../utils/matches.js'
|
||||
import { computeDependenciesBySortingNode as computeDependenciesBySortingNode$1 } from '../../utils/compute-dependencies-by-sorting-node.js'
|
||||
import { computeParentNodesWithTypes } from '../../utils/compute-parent-nodes-with-types.js'
|
||||
import { computeIdentifierNameDetails } from './compute-identifier-name-details.js'
|
||||
import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils'
|
||||
function computeDependenciesBySortingNode({
|
||||
ignoreCallbackDependenciesPatterns,
|
||||
sortingNodes,
|
||||
sourceCode,
|
||||
classBody,
|
||||
}) {
|
||||
let dependenciesBySortingNode = computeDependenciesBySortingNode$1({
|
||||
additionalIdentifierDependenciesComputer:
|
||||
buildAdditionalIdentifierDependenciesComputer({
|
||||
ignoreCallbackDependenciesPatterns,
|
||||
staticSortingNodes: sortingNodes.filter(node => node.isStatic),
|
||||
classBody,
|
||||
}),
|
||||
shouldIgnoreSortingNodeComputer: sortingNode =>
|
||||
shouldIgnoreDependencyComputation(sortingNode.node),
|
||||
sortingNodes,
|
||||
sourceCode,
|
||||
})
|
||||
let thisDependenciesBySortingNode =
|
||||
computeThisExpressionDependenciesBySortingNode({
|
||||
ignoreCallbackDependenciesPatterns,
|
||||
sortingNodes,
|
||||
sourceCode,
|
||||
})
|
||||
for (let [sortingNode, dependencies] of thisDependenciesBySortingNode) {
|
||||
let existingDependencies = dependenciesBySortingNode.get(sortingNode) ?? []
|
||||
dependenciesBySortingNode.set(sortingNode, [
|
||||
...existingDependencies,
|
||||
...dependencies,
|
||||
])
|
||||
}
|
||||
return dependenciesBySortingNode
|
||||
}
|
||||
function computeIdentifierOrThisExpressionDependency({
|
||||
ignoreCallbackDependenciesPatterns,
|
||||
sortingNodes,
|
||||
classElement,
|
||||
node,
|
||||
}) {
|
||||
if (shouldIgnoreCallbackDependency()) {
|
||||
return null
|
||||
}
|
||||
let { parent } = node
|
||||
/* v8 ignore if -- @preserve Unsure how we can reach that case */
|
||||
if (parent.type !== AST_NODE_TYPES.MemberExpression) {
|
||||
return null
|
||||
}
|
||||
let dependencyName = computeDependencyNameFromMemberExpression(parent)
|
||||
/* v8 ignore if -- @preserve Unsure how we can reach that case */
|
||||
if (!dependencyName) {
|
||||
return null
|
||||
}
|
||||
return (
|
||||
sortingNodes.find(
|
||||
currentSortingNode => currentSortingNode.name === dependencyName.name,
|
||||
) ?? null
|
||||
)
|
||||
function computeDependencyNameFromMemberExpression(memberExpression) {
|
||||
switch (memberExpression.property.type) {
|
||||
case AST_NODE_TYPES.PrivateIdentifier:
|
||||
case AST_NODE_TYPES.Identifier:
|
||||
case AST_NODE_TYPES.Literal:
|
||||
return computeIdentifierNameDetails(memberExpression.property)
|
||||
/* v8 ignore next 2 -- @preserve Unhandled cases */
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
function shouldIgnoreCallbackDependency() {
|
||||
let [firstCallExpressionParent] = computeParentNodesWithTypes({
|
||||
allowedTypes: [AST_NODE_TYPES.CallExpression],
|
||||
maxParent: classElement,
|
||||
consecutiveOnly: false,
|
||||
node,
|
||||
})
|
||||
if (!firstCallExpressionParent) {
|
||||
return false
|
||||
}
|
||||
if (!('name' in firstCallExpressionParent.callee)) {
|
||||
return false
|
||||
}
|
||||
return matches(
|
||||
firstCallExpressionParent.callee.name,
|
||||
ignoreCallbackDependenciesPatterns,
|
||||
)
|
||||
}
|
||||
}
|
||||
function computeThisExpressionDependenciesBySortingNode({
|
||||
ignoreCallbackDependenciesPatterns,
|
||||
sortingNodes,
|
||||
sourceCode,
|
||||
}) {
|
||||
let dependenciesBySortingNode = /* @__PURE__ */ new Map()
|
||||
let staticSortingNodes = sortingNodes.filter(node => node.isStatic)
|
||||
let nonStaticSortingNodes = sortingNodes.filter(node => !node.isStatic)
|
||||
let relevantSortingNodes = sortingNodes.filter(
|
||||
sortingNode => !shouldIgnoreDependencyComputation(sortingNode.node),
|
||||
)
|
||||
for (let sortingNode of relevantSortingNodes) {
|
||||
let dependencies = computeThisExpressionsInsideClassElement({
|
||||
classElement: sortingNode.node,
|
||||
sourceCode,
|
||||
})
|
||||
.map(thisExpression =>
|
||||
computeIdentifierOrThisExpressionDependency({
|
||||
sortingNodes:
|
||||
sortingNode.isStatic ? staticSortingNodes : nonStaticSortingNodes,
|
||||
ignoreCallbackDependenciesPatterns,
|
||||
classElement: sortingNode.node,
|
||||
node: thisExpression,
|
||||
}),
|
||||
)
|
||||
.filter(dependency => dependency !== null)
|
||||
if (dependencies.length === 0) {
|
||||
continue
|
||||
}
|
||||
dependenciesBySortingNode.set(sortingNode, dependencies)
|
||||
}
|
||||
return dependenciesBySortingNode
|
||||
}
|
||||
function buildAdditionalIdentifierDependenciesComputer({
|
||||
ignoreCallbackDependenciesPatterns,
|
||||
staticSortingNodes,
|
||||
classBody,
|
||||
}) {
|
||||
return ({ referencingSortingNode, reference }) => {
|
||||
if (!reference.resolved?.identifiers[0]) {
|
||||
return []
|
||||
}
|
||||
let classIdentifier = classBody.parent.id
|
||||
if (reference.resolved?.identifiers[0] !== classIdentifier) {
|
||||
return []
|
||||
}
|
||||
let dependency = computeIdentifierOrThisExpressionDependency({
|
||||
classElement: referencingSortingNode.node,
|
||||
ignoreCallbackDependenciesPatterns,
|
||||
sortingNodes: staticSortingNodes,
|
||||
node: reference.identifier,
|
||||
})
|
||||
return dependency ? [dependency] : []
|
||||
}
|
||||
}
|
||||
function shouldIgnoreDependencyComputation(node) {
|
||||
switch (node.type) {
|
||||
case AST_NODE_TYPES.TSAbstractPropertyDefinition:
|
||||
case AST_NODE_TYPES.TSAbstractMethodDefinition:
|
||||
case AST_NODE_TYPES.StaticBlock:
|
||||
return false
|
||||
case AST_NODE_TYPES.TSAbstractAccessorProperty:
|
||||
case AST_NODE_TYPES.AccessorProperty:
|
||||
case AST_NODE_TYPES.MethodDefinition:
|
||||
case AST_NODE_TYPES.TSIndexSignature:
|
||||
return true
|
||||
case AST_NODE_TYPES.PropertyDefinition:
|
||||
return (
|
||||
node.value?.type === AST_NODE_TYPES.ArrowFunctionExpression ||
|
||||
node.value?.type === AST_NODE_TYPES.FunctionExpression
|
||||
)
|
||||
/* v8 ignore next 2 -- @preserve Exhaustive guard. */
|
||||
default:
|
||||
throw new UnreachableCaseError(node)
|
||||
}
|
||||
}
|
||||
function computeThisExpressionsInsideClassElement({
|
||||
classElement,
|
||||
sourceCode,
|
||||
}) {
|
||||
return sourceCode
|
||||
.getTokens(classElement)
|
||||
.filter(isThisToken)
|
||||
.map(computeTokenNode)
|
||||
.filter(node => node?.type === AST_NODE_TYPES.ThisExpression)
|
||||
function computeTokenNode(token) {
|
||||
return sourceCode.getNodeByRangeIndex(token.range[0])
|
||||
}
|
||||
function isThisToken(token) {
|
||||
return token.type === AST_TOKEN_TYPES.Keyword && token.value === 'this'
|
||||
}
|
||||
}
|
||||
export { computeDependenciesBySortingNode }
|
||||
Generated
Vendored
+29
@@ -0,0 +1,29 @@
|
||||
import { TSESTree } from '@typescript-eslint/types'
|
||||
import { RegexOption } from '../../types/common-options.js'
|
||||
/**
|
||||
* Computes the dependencies of a class member AST node.
|
||||
*
|
||||
* @deprecated - To remove when experimental dependency detection is the only.
|
||||
* @param params - Parameters object.
|
||||
* @param params.ignoreCallbackDependenciesPatterns - Patterns to ignore
|
||||
* callback dependencies.
|
||||
* @param params.useExperimentalDependencyDetection - Whether to use
|
||||
* experimental dependency detection.
|
||||
* @param params.isMemberStatic - Indicates if the member is static.
|
||||
* @param params.expression - The AST node expression to analyze.
|
||||
* @param params.className - The name of the class, if available.
|
||||
* @returns The names of the dependencies.
|
||||
*/
|
||||
export declare function computeDependencies({
|
||||
ignoreCallbackDependenciesPatterns,
|
||||
useExperimentalDependencyDetection,
|
||||
isMemberStatic,
|
||||
expression,
|
||||
className,
|
||||
}: {
|
||||
expression: TSESTree.StaticBlock | TSESTree.Expression
|
||||
ignoreCallbackDependenciesPatterns: RegexOption
|
||||
useExperimentalDependencyDetection: boolean
|
||||
className: undefined | string
|
||||
isMemberStatic: boolean
|
||||
}): string[]
|
||||
Generated
Vendored
+166
@@ -0,0 +1,166 @@
|
||||
import { matches } from '../../utils/matches.js'
|
||||
import { computeIdentifierNameDetails } from './compute-identifier-name-details.js'
|
||||
import { computeDependencyName } from './compute-dependency-name.js'
|
||||
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
|
||||
/**
|
||||
* Computes the dependencies of a class member AST node.
|
||||
*
|
||||
* @deprecated - To remove when experimental dependency detection is the only.
|
||||
* @param params - Parameters object.
|
||||
* @param params.ignoreCallbackDependenciesPatterns - Patterns to ignore
|
||||
* callback dependencies.
|
||||
* @param params.useExperimentalDependencyDetection - Whether to use
|
||||
* experimental dependency detection.
|
||||
* @param params.isMemberStatic - Indicates if the member is static.
|
||||
* @param params.expression - The AST node expression to analyze.
|
||||
* @param params.className - The name of the class, if available.
|
||||
* @returns The names of the dependencies.
|
||||
*/
|
||||
function computeDependencies({
|
||||
ignoreCallbackDependenciesPatterns,
|
||||
useExperimentalDependencyDetection,
|
||||
isMemberStatic,
|
||||
expression,
|
||||
className,
|
||||
}) {
|
||||
if (useExperimentalDependencyDetection) {
|
||||
return []
|
||||
}
|
||||
let dependencies = []
|
||||
traverseNode(expression)
|
||||
return dependencies
|
||||
function checkNode(nodeValue) {
|
||||
switch (nodeValue.type) {
|
||||
case AST_NODE_TYPES.ConditionalExpression:
|
||||
traverseNode(nodeValue.test)
|
||||
traverseNode(nodeValue.consequent)
|
||||
traverseNode(nodeValue.alternate)
|
||||
break
|
||||
case AST_NODE_TYPES.MemberExpression:
|
||||
dependencies.push(
|
||||
...computeMemberExpressionDependencies({
|
||||
memberExpression: nodeValue,
|
||||
isMemberStatic,
|
||||
className,
|
||||
}),
|
||||
)
|
||||
break
|
||||
case AST_NODE_TYPES.CallExpression:
|
||||
if (!('name' in nodeValue.callee)) {
|
||||
traverseNode(nodeValue.arguments)
|
||||
break
|
||||
}
|
||||
if (
|
||||
matches(nodeValue.callee.name, ignoreCallbackDependenciesPatterns)
|
||||
) {
|
||||
break
|
||||
}
|
||||
traverseNode(nodeValue.arguments)
|
||||
break
|
||||
case AST_NODE_TYPES.NewExpression:
|
||||
traverseNode(nodeValue.arguments)
|
||||
break
|
||||
case AST_NODE_TYPES.Property:
|
||||
traverseNode(nodeValue.key)
|
||||
traverseNode(nodeValue.value)
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
if ('argument' in nodeValue && nodeValue.argument) {
|
||||
traverseNode(nodeValue.argument)
|
||||
}
|
||||
if ('body' in nodeValue && nodeValue.body) {
|
||||
traverseNode(nodeValue.body)
|
||||
}
|
||||
if ('callee' in nodeValue) {
|
||||
traverseNode(nodeValue.callee)
|
||||
}
|
||||
if ('declarations' in nodeValue) {
|
||||
traverseNode(nodeValue.declarations)
|
||||
}
|
||||
if ('elements' in nodeValue) {
|
||||
traverseNode(
|
||||
nodeValue.elements.filter(currentNode => currentNode !== null),
|
||||
)
|
||||
}
|
||||
if (
|
||||
'expression' in nodeValue &&
|
||||
typeof nodeValue.expression !== 'boolean'
|
||||
) {
|
||||
traverseNode(nodeValue.expression)
|
||||
}
|
||||
if ('expressions' in nodeValue) {
|
||||
traverseNode(nodeValue.expressions)
|
||||
}
|
||||
if ('init' in nodeValue && nodeValue.init) {
|
||||
traverseNode(nodeValue.init)
|
||||
}
|
||||
if ('left' in nodeValue) {
|
||||
traverseNode(nodeValue.left)
|
||||
}
|
||||
if ('object' in nodeValue) {
|
||||
traverseNode(nodeValue.object)
|
||||
}
|
||||
if ('properties' in nodeValue) {
|
||||
traverseNode(nodeValue.properties)
|
||||
}
|
||||
if ('right' in nodeValue) {
|
||||
traverseNode(nodeValue.right)
|
||||
}
|
||||
}
|
||||
function traverseNode(nodeValue) {
|
||||
if (Array.isArray(nodeValue)) {
|
||||
for (let nodeItem of nodeValue) {
|
||||
traverseNode(nodeItem)
|
||||
}
|
||||
} else {
|
||||
checkNode(nodeValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
function computeMemberExpressionDependencies({
|
||||
memberExpression,
|
||||
isMemberStatic,
|
||||
className,
|
||||
}) {
|
||||
switch (memberExpression.object.type) {
|
||||
case AST_NODE_TYPES.ThisExpression:
|
||||
return computeIdentifierOrThisExpressionDependencies()
|
||||
case AST_NODE_TYPES.Identifier:
|
||||
return memberExpression.object.name === className ?
|
||||
computeIdentifierOrThisExpressionDependencies()
|
||||
: []
|
||||
default:
|
||||
return []
|
||||
}
|
||||
function computeIdentifierOrThisExpressionDependencies() {
|
||||
let dependency = computeDependencyFromProperty()
|
||||
/* v8 ignore next 2 -- @preserve Unhandled cases */
|
||||
if (!dependency) {
|
||||
return []
|
||||
}
|
||||
return [dependency]
|
||||
}
|
||||
function computeDependencyFromProperty() {
|
||||
switch (memberExpression.property.type) {
|
||||
case AST_NODE_TYPES.PrivateIdentifier:
|
||||
case AST_NODE_TYPES.Identifier:
|
||||
case AST_NODE_TYPES.Literal: {
|
||||
let { nameWithoutStartingHash, hasPrivateHash } =
|
||||
computeIdentifierNameDetails(memberExpression.property)
|
||||
return computeDependencyName({
|
||||
isStatic:
|
||||
isMemberStatic ||
|
||||
memberExpression.object.type === AST_NODE_TYPES.Identifier,
|
||||
nodeNameWithoutStartingHash: nameWithoutStartingHash,
|
||||
hasPrivateHash,
|
||||
})
|
||||
}
|
||||
/* v8 ignore next 2 -- @preserve Unhandled cases */
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
export { computeDependencies }
|
||||
Generated
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
export declare function computeDependencyName({
|
||||
nodeNameWithoutStartingHash,
|
||||
hasPrivateHash,
|
||||
isStatic,
|
||||
}: {
|
||||
nodeNameWithoutStartingHash: string
|
||||
hasPrivateHash: boolean
|
||||
isStatic: boolean
|
||||
}): string
|
||||
frontend/node_modules/eslint-plugin-perfectionist/dist/rules/sort-classes/compute-dependency-name.js
Generated
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
function computeDependencyName({
|
||||
nodeNameWithoutStartingHash,
|
||||
hasPrivateHash,
|
||||
isStatic,
|
||||
}) {
|
||||
return `${isStatic ? 'static ' : ''}${hasPrivateHash ? '#' : ''}${nodeNameWithoutStartingHash}`
|
||||
}
|
||||
export { computeDependencyName }
|
||||
Generated
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
import { TSESTree } from '@typescript-eslint/types'
|
||||
import { NodeNameDetails } from './types.js'
|
||||
/**
|
||||
* Computes the name details of an identifier.
|
||||
*
|
||||
* @param node - The node to compute the name details for.
|
||||
* @returns An object containing the name, whether it has a private hash, and
|
||||
* the name without the starting hash.
|
||||
*/
|
||||
export declare function computeIdentifierNameDetails(
|
||||
node: TSESTree.PrivateIdentifier | TSESTree.Identifier | TSESTree.Literal,
|
||||
): NodeNameDetails
|
||||
Generated
Vendored
+34
@@ -0,0 +1,34 @@
|
||||
import { UnreachableCaseError } from '../../utils/unreachable-case-error.js'
|
||||
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
|
||||
/**
|
||||
* Computes the name details of an identifier.
|
||||
*
|
||||
* @param node - The node to compute the name details for.
|
||||
* @returns An object containing the name, whether it has a private hash, and
|
||||
* the name without the starting hash.
|
||||
*/
|
||||
function computeIdentifierNameDetails(node) {
|
||||
switch (node.type) {
|
||||
case AST_NODE_TYPES.PrivateIdentifier:
|
||||
return {
|
||||
nameWithoutStartingHash: node.name,
|
||||
name: `#${node.name}`,
|
||||
hasPrivateHash: true,
|
||||
}
|
||||
case AST_NODE_TYPES.Identifier:
|
||||
return buildNonPrivateHashDetails(node.name)
|
||||
case AST_NODE_TYPES.Literal:
|
||||
return buildNonPrivateHashDetails(`${node.value}`)
|
||||
/* v8 ignore next 2 -- @preserve Exhaustive guard. */
|
||||
default:
|
||||
throw new UnreachableCaseError(node)
|
||||
}
|
||||
}
|
||||
function buildNonPrivateHashDetails(name) {
|
||||
return {
|
||||
nameWithoutStartingHash: name,
|
||||
hasPrivateHash: false,
|
||||
name,
|
||||
}
|
||||
}
|
||||
export { computeIdentifierNameDetails }
|
||||
Generated
Vendored
+23
@@ -0,0 +1,23 @@
|
||||
import { RuleContext } from '@typescript-eslint/utils/ts-eslint'
|
||||
import { TSESTree } from '@typescript-eslint/types'
|
||||
import { Options } from './types.js'
|
||||
/**
|
||||
* Computes the matched context options for a given class node.
|
||||
*
|
||||
* @param params - Parameters.
|
||||
* @param params.matchedAstSelectors - The matched AST selectors for a class
|
||||
* node.
|
||||
* @param params.context - The rule context.
|
||||
* @returns The matched context options or undefined if none match.
|
||||
*/
|
||||
export declare function computeMatchedContextOptions<
|
||||
MessageIds extends string,
|
||||
>({
|
||||
matchedAstSelectors,
|
||||
classElements,
|
||||
context,
|
||||
}: {
|
||||
context: Readonly<RuleContext<MessageIds, Options>>
|
||||
matchedAstSelectors: ReadonlySet<string>
|
||||
classElements: TSESTree.ClassElement[]
|
||||
}): Options[number] | undefined
|
||||
Generated
Vendored
+52
@@ -0,0 +1,52 @@
|
||||
import { passesAllNamesMatchPatternFilter } from '../../utils/context-matching/passes-all-names-match-pattern-filter.js'
|
||||
import { passesAstSelectorFilter } from '../../utils/context-matching/passes-ast-selector-filter.js'
|
||||
import { computeMethodOrPropertyNameDetails } from './node-info/compute-method-or-property-name-details.js'
|
||||
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
|
||||
/**
|
||||
* Computes the matched context options for a given class node.
|
||||
*
|
||||
* @param params - Parameters.
|
||||
* @param params.matchedAstSelectors - The matched AST selectors for a class
|
||||
* node.
|
||||
* @param params.context - The rule context.
|
||||
* @returns The matched context options or undefined if none match.
|
||||
*/
|
||||
function computeMatchedContextOptions({
|
||||
matchedAstSelectors,
|
||||
classElements,
|
||||
context,
|
||||
}) {
|
||||
let nodeNames = classElements
|
||||
.filter(
|
||||
element =>
|
||||
element.type !== AST_NODE_TYPES.StaticBlock &&
|
||||
element.type !== AST_NODE_TYPES.TSIndexSignature,
|
||||
)
|
||||
.map(
|
||||
element =>
|
||||
computeMethodOrPropertyNameDetails(element, context.sourceCode).name,
|
||||
)
|
||||
return context.options.find(options =>
|
||||
isContextOptionMatching({
|
||||
matchedAstSelectors,
|
||||
nodeNames,
|
||||
options,
|
||||
}),
|
||||
)
|
||||
}
|
||||
function isContextOptionMatching({ matchedAstSelectors, nodeNames, options }) {
|
||||
if (!options.useConfigurationIf) {
|
||||
return true
|
||||
}
|
||||
return (
|
||||
passesAllNamesMatchPatternFilter({
|
||||
allNamesMatchPattern: options.useConfigurationIf.allNamesMatchPattern,
|
||||
nodeNames,
|
||||
}) &&
|
||||
passesAstSelectorFilter({
|
||||
matchesAstSelector: options.useConfigurationIf.matchesAstSelector,
|
||||
matchedAstSelectors,
|
||||
})
|
||||
)
|
||||
}
|
||||
export { computeMatchedContextOptions }
|
||||
Generated
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
import { TSESTree } from '@typescript-eslint/utils'
|
||||
import { OverloadSignatureGroup } from '../../utils/overload-signature/overload-signature-group.js'
|
||||
type Method = TSESTree.TSAbstractMethodDefinition | TSESTree.MethodDefinition
|
||||
/**
|
||||
* Returns a list of groups of overload signatures.
|
||||
*
|
||||
* @param classElements - The class elements to process.
|
||||
* @returns A list of overload signature groups.
|
||||
*/
|
||||
export declare function computeOverloadSignatureGroups(
|
||||
classElements: TSESTree.ClassElement[],
|
||||
): OverloadSignatureGroup<Method>[]
|
||||
export {}
|
||||
Generated
Vendored
+62
@@ -0,0 +1,62 @@
|
||||
import { UnreachableCaseError } from '../../utils/unreachable-case-error.js'
|
||||
import { isSortable } from '../../utils/is-sortable.js'
|
||||
import { OverloadSignatureGroup } from '../../utils/overload-signature/overload-signature-group.js'
|
||||
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
|
||||
/**
|
||||
* Returns a list of groups of overload signatures.
|
||||
*
|
||||
* @param classElements - The class elements to process.
|
||||
* @returns A list of overload signature groups.
|
||||
*/
|
||||
function computeOverloadSignatureGroups(classElements) {
|
||||
let methods = classElements
|
||||
.filter(
|
||||
classElement =>
|
||||
classElement.type === AST_NODE_TYPES.MethodDefinition ||
|
||||
classElement.type === AST_NODE_TYPES.TSAbstractMethodDefinition,
|
||||
)
|
||||
.filter(classElement => classElement.kind === 'method')
|
||||
let staticOverloadSignaturesByName = /* @__PURE__ */ new Map()
|
||||
let overloadSignaturesByName = /* @__PURE__ */ new Map()
|
||||
for (let method of methods) {
|
||||
if (method.key.type !== AST_NODE_TYPES.Identifier) {
|
||||
continue
|
||||
}
|
||||
let { name } = method.key
|
||||
let mapToUse =
|
||||
method.static ? staticOverloadSignaturesByName : overloadSignaturesByName
|
||||
let overloadSignaturesArray = mapToUse.get(name)
|
||||
if (!overloadSignaturesArray) {
|
||||
overloadSignaturesArray = []
|
||||
mapToUse.set(name, overloadSignaturesArray)
|
||||
}
|
||||
overloadSignaturesArray.push(method)
|
||||
}
|
||||
return [
|
||||
...overloadSignaturesByName.values(),
|
||||
...staticOverloadSignaturesByName.values(),
|
||||
]
|
||||
.filter(isSortable)
|
||||
.map(buildOverloadSignatureGroup)
|
||||
}
|
||||
function buildOverloadSignatureGroup(methods) {
|
||||
let implementation = methods.find(isMethodImplementation) ?? methods.at(-1)
|
||||
return new OverloadSignatureGroup({
|
||||
overloadSignatures: methods.filter(
|
||||
method => !isMethodImplementation(method),
|
||||
),
|
||||
implementation,
|
||||
})
|
||||
function isMethodImplementation(method) {
|
||||
switch (method.value.type) {
|
||||
case AST_NODE_TYPES.TSEmptyBodyFunctionExpression:
|
||||
return false
|
||||
case AST_NODE_TYPES.FunctionExpression:
|
||||
return true
|
||||
/* v8 ignore next 2 -- @preserve Exhaustive guard. */
|
||||
default:
|
||||
throw new UnreachableCaseError(method.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
export { computeOverloadSignatureGroups }
|
||||
Generated
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
import { TSESTree } from '@typescript-eslint/types'
|
||||
/**
|
||||
* Checks whether a class element is supported by the sort-classes rule.
|
||||
*
|
||||
* Unknown elements should be ignored to avoid crashes with non-standard parsers
|
||||
* while letting known elements keep their ordering behavior.
|
||||
*
|
||||
* @param member - The class element to check.
|
||||
* @returns True when the element is a known, supported class member.
|
||||
*/
|
||||
export declare function isKnownClassElement(
|
||||
member: TSESTree.ClassElement,
|
||||
): boolean
|
||||
Generated
Vendored
+27
@@ -0,0 +1,27 @@
|
||||
import '../../utils/assert-is-never.js'
|
||||
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
|
||||
/**
|
||||
* Checks whether a class element is supported by the sort-classes rule.
|
||||
*
|
||||
* Unknown elements should be ignored to avoid crashes with non-standard parsers
|
||||
* while letting known elements keep their ordering behavior.
|
||||
*
|
||||
* @param member - The class element to check.
|
||||
* @returns True when the element is a known, supported class member.
|
||||
*/
|
||||
function isKnownClassElement(member) {
|
||||
switch (member.type) {
|
||||
case AST_NODE_TYPES.TSAbstractPropertyDefinition:
|
||||
case AST_NODE_TYPES.TSAbstractMethodDefinition:
|
||||
case AST_NODE_TYPES.TSAbstractAccessorProperty:
|
||||
case AST_NODE_TYPES.PropertyDefinition:
|
||||
case AST_NODE_TYPES.MethodDefinition:
|
||||
case AST_NODE_TYPES.AccessorProperty:
|
||||
case AST_NODE_TYPES.TSIndexSignature:
|
||||
case AST_NODE_TYPES.StaticBlock:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
export { isKnownClassElement }
|
||||
Generated
Vendored
+39
@@ -0,0 +1,39 @@
|
||||
import { TSESTree } from '@typescript-eslint/types'
|
||||
import { Modifier } from '../types.js'
|
||||
type Property =
|
||||
| TSESTree.TSAbstractPropertyDefinition
|
||||
| TSESTree.PropertyDefinition
|
||||
type Accessor = TSESTree.TSAbstractAccessorProperty | TSESTree.AccessorProperty
|
||||
type Method = TSESTree.TSAbstractMethodDefinition | TSESTree.MethodDefinition
|
||||
export declare function computeAccessibilityModifier({
|
||||
hasPrivateHash,
|
||||
node,
|
||||
}: {
|
||||
node: Accessor | Property | Method
|
||||
hasPrivateHash: boolean
|
||||
}): Modifier[]
|
||||
export declare function computeAbstractModifier(
|
||||
node: Accessor | Property | Method,
|
||||
): Modifier[]
|
||||
export declare function computeAsyncModifier(
|
||||
node:
|
||||
| TSESTree.TSEmptyBodyFunctionExpression
|
||||
| TSESTree.ArrowFunctionExpression
|
||||
| TSESTree.FunctionExpression,
|
||||
): Modifier[]
|
||||
export declare function computeStaticModifier(
|
||||
node: TSESTree.TSIndexSignature | Accessor | Property | Method,
|
||||
): Modifier[]
|
||||
export declare function computeReadonlyModifier(
|
||||
node: TSESTree.TSIndexSignature | Property,
|
||||
): Modifier[]
|
||||
export declare function computeOverrideModifier(
|
||||
node: Accessor | Property | Method,
|
||||
): Modifier[]
|
||||
export declare function computeOptionalModifier(
|
||||
node: Property | Method,
|
||||
): Modifier[]
|
||||
export declare function computeDecoratedModifier(
|
||||
isDecorated: boolean,
|
||||
): Modifier[]
|
||||
export {}
|
||||
Generated
Vendored
+71
@@ -0,0 +1,71 @@
|
||||
import { UnreachableCaseError } from '../../../utils/unreachable-case-error.js'
|
||||
import '../../../utils/assert-is-never.js'
|
||||
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
|
||||
function computeAccessibilityModifier({ hasPrivateHash, node }) {
|
||||
if (hasPrivateHash) {
|
||||
return ['private']
|
||||
}
|
||||
switch (node.accessibility) {
|
||||
case 'protected':
|
||||
return ['protected']
|
||||
case 'private':
|
||||
return ['private']
|
||||
case void 0:
|
||||
case 'public':
|
||||
return ['public']
|
||||
default:
|
||||
node.accessibility
|
||||
return computeUnhandledAccessibilityModifier(node.accessibility)
|
||||
}
|
||||
function computeUnhandledAccessibilityModifier(modifier) {
|
||||
/* v8 ignore else -- @preserve Unhandled case */
|
||||
if (modifier === null) {
|
||||
return ['public']
|
||||
}
|
||||
/* v8 ignore next -- @preserve Unhandled case */
|
||||
throw new Error('Unhandled accessibility modifier')
|
||||
}
|
||||
}
|
||||
function computeAbstractModifier(node) {
|
||||
switch (node.type) {
|
||||
case AST_NODE_TYPES.TSAbstractPropertyDefinition:
|
||||
case AST_NODE_TYPES.TSAbstractMethodDefinition:
|
||||
case AST_NODE_TYPES.TSAbstractAccessorProperty:
|
||||
return ['abstract']
|
||||
case AST_NODE_TYPES.PropertyDefinition:
|
||||
case AST_NODE_TYPES.MethodDefinition:
|
||||
case AST_NODE_TYPES.AccessorProperty:
|
||||
return []
|
||||
/* v8 ignore next 2 -- @preserve Exhaustive guard. */
|
||||
default:
|
||||
throw new UnreachableCaseError(node)
|
||||
}
|
||||
}
|
||||
function computeAsyncModifier(node) {
|
||||
return node.async ? ['async'] : []
|
||||
}
|
||||
function computeStaticModifier(node) {
|
||||
return node.static ? ['static'] : []
|
||||
}
|
||||
function computeReadonlyModifier(node) {
|
||||
return node.readonly ? ['readonly'] : []
|
||||
}
|
||||
function computeOverrideModifier(node) {
|
||||
return node.override ? ['override'] : []
|
||||
}
|
||||
function computeOptionalModifier(node) {
|
||||
return node.optional ? ['optional'] : []
|
||||
}
|
||||
function computeDecoratedModifier(isDecorated) {
|
||||
return isDecorated ? ['decorated'] : []
|
||||
}
|
||||
export {
|
||||
computeAbstractModifier,
|
||||
computeAccessibilityModifier,
|
||||
computeAsyncModifier,
|
||||
computeDecoratedModifier,
|
||||
computeOptionalModifier,
|
||||
computeOverrideModifier,
|
||||
computeReadonlyModifier,
|
||||
computeStaticModifier,
|
||||
}
|
||||
Generated
Vendored
+27
@@ -0,0 +1,27 @@
|
||||
import { TSESTree } from '@typescript-eslint/types'
|
||||
import { TSESLint } from '@typescript-eslint/utils'
|
||||
import { NodeNameDetails, Modifier, Selector } from '../types.js'
|
||||
/**
|
||||
* Computes details related to an accessor property.
|
||||
*
|
||||
* @param params - Parameters object.
|
||||
* @param params.isDecorated - Whether the accessor is decorated.
|
||||
* @param params.sourceCode - The source code object.
|
||||
* @param params.accessor - The accessor node to compute information for.
|
||||
* @returns An object containing various details about the accessor.
|
||||
*/
|
||||
export declare function computeAccessorDetails({
|
||||
isDecorated,
|
||||
sourceCode,
|
||||
accessor,
|
||||
}: {
|
||||
accessor: TSESTree.TSAbstractAccessorProperty | TSESTree.AccessorProperty
|
||||
sourceCode: TSESLint.SourceCode
|
||||
isDecorated: boolean
|
||||
}): {
|
||||
nameDetails: NodeNameDetails
|
||||
dependencyNames: string[]
|
||||
modifiers: Modifier[]
|
||||
selectors: Selector[]
|
||||
isStatic: boolean
|
||||
}
|
||||
Generated
Vendored
+52
@@ -0,0 +1,52 @@
|
||||
import {
|
||||
computeAbstractModifier,
|
||||
computeAccessibilityModifier,
|
||||
computeDecoratedModifier,
|
||||
computeOverrideModifier,
|
||||
computeStaticModifier,
|
||||
} from './common-modifiers.js'
|
||||
import { computeDependencyName } from '../compute-dependency-name.js'
|
||||
import { computeMethodOrPropertyNameDetails } from './compute-method-or-property-name-details.js'
|
||||
/**
|
||||
* Computes details related to an accessor property.
|
||||
*
|
||||
* @param params - Parameters object.
|
||||
* @param params.isDecorated - Whether the accessor is decorated.
|
||||
* @param params.sourceCode - The source code object.
|
||||
* @param params.accessor - The accessor node to compute information for.
|
||||
* @returns An object containing various details about the accessor.
|
||||
*/
|
||||
function computeAccessorDetails({ isDecorated, sourceCode, accessor }) {
|
||||
let nameDetails = computeMethodOrPropertyNameDetails(accessor, sourceCode)
|
||||
let modifiers = computeModifiers({
|
||||
hasPrivateHash: nameDetails.hasPrivateHash,
|
||||
isDecorated,
|
||||
accessor,
|
||||
})
|
||||
return {
|
||||
dependencyNames: [
|
||||
computeDependencyName({
|
||||
nodeNameWithoutStartingHash: nameDetails.nameWithoutStartingHash,
|
||||
hasPrivateHash: nameDetails.hasPrivateHash,
|
||||
isStatic: modifiers.includes('static'),
|
||||
}),
|
||||
],
|
||||
selectors: ['accessor-property'],
|
||||
isStatic: accessor.static,
|
||||
nameDetails,
|
||||
modifiers,
|
||||
}
|
||||
}
|
||||
function computeModifiers({ hasPrivateHash, isDecorated, accessor }) {
|
||||
return [
|
||||
...computeStaticModifier(accessor),
|
||||
...computeAbstractModifier(accessor),
|
||||
...computeDecoratedModifier(isDecorated),
|
||||
...computeOverrideModifier(accessor),
|
||||
...computeAccessibilityModifier({
|
||||
hasPrivateHash,
|
||||
node: accessor,
|
||||
}),
|
||||
]
|
||||
}
|
||||
export { computeAccessorDetails }
|
||||
Generated
Vendored
+23
@@ -0,0 +1,23 @@
|
||||
import { TSESTree } from '@typescript-eslint/types'
|
||||
import { TSESLint } from '@typescript-eslint/utils'
|
||||
import { Modifier, Selector } from '../types.js'
|
||||
/**
|
||||
* Computes details related to an index-signature.
|
||||
*
|
||||
* @param params - Parameters object.
|
||||
* @param params.indexSignature - The index signature node to compute
|
||||
* information for.
|
||||
* @param params.sourceCode - The source code object.
|
||||
* @returns An object containing various details about the index-signature.
|
||||
*/
|
||||
export declare function computeIndexSignatureDetails({
|
||||
indexSignature,
|
||||
sourceCode,
|
||||
}: {
|
||||
indexSignature: TSESTree.TSIndexSignature
|
||||
sourceCode: TSESLint.SourceCode
|
||||
}): {
|
||||
modifiers: Modifier[]
|
||||
selectors: Selector[]
|
||||
name: string
|
||||
}
|
||||
Generated
Vendored
+30
@@ -0,0 +1,30 @@
|
||||
import {
|
||||
computeReadonlyModifier,
|
||||
computeStaticModifier,
|
||||
} from './common-modifiers.js'
|
||||
/**
|
||||
* Computes details related to an index-signature.
|
||||
*
|
||||
* @param params - Parameters object.
|
||||
* @param params.indexSignature - The index signature node to compute
|
||||
* information for.
|
||||
* @param params.sourceCode - The source code object.
|
||||
* @returns An object containing various details about the index-signature.
|
||||
*/
|
||||
function computeIndexSignatureDetails({ indexSignature, sourceCode }) {
|
||||
return {
|
||||
name: sourceCode.text.slice(
|
||||
indexSignature.range.at(0),
|
||||
indexSignature.typeAnnotation?.range.at(0) ?? indexSignature.range.at(1),
|
||||
),
|
||||
modifiers: computeModifiers(indexSignature),
|
||||
selectors: ['index-signature'],
|
||||
}
|
||||
}
|
||||
function computeModifiers(indexSignature) {
|
||||
return [
|
||||
...computeStaticModifier(indexSignature),
|
||||
...computeReadonlyModifier(indexSignature),
|
||||
]
|
||||
}
|
||||
export { computeIndexSignatureDetails }
|
||||
Generated
Vendored
+30
@@ -0,0 +1,30 @@
|
||||
import { TSESTree } from '@typescript-eslint/types'
|
||||
import { TSESLint } from '@typescript-eslint/utils'
|
||||
import { NodeNameDetails, Modifier, Selector } from '../types.js'
|
||||
/**
|
||||
* Computes details related to a method.
|
||||
*
|
||||
* @param params - Parameters object.
|
||||
* @param params.isDecorated - Whether the accessor is decorated.
|
||||
* @param params.method - The method node to compute information for.
|
||||
* @param params.sourceCode - The source code object.
|
||||
* @param params.hasParentDeclare - Whether the parent class is a declare class.
|
||||
* @returns An object containing various details about the method.
|
||||
*/
|
||||
export declare function computeMethodDetails({
|
||||
hasParentDeclare,
|
||||
isDecorated,
|
||||
sourceCode,
|
||||
method,
|
||||
}: {
|
||||
method: TSESTree.TSAbstractMethodDefinition | TSESTree.MethodDefinition
|
||||
sourceCode: TSESLint.SourceCode
|
||||
hasParentDeclare: boolean
|
||||
isDecorated: boolean
|
||||
}): {
|
||||
addSafetySemicolonWhenInline: boolean
|
||||
nameDetails: NodeNameDetails
|
||||
modifiers: Modifier[]
|
||||
selectors: Selector[]
|
||||
isStatic: boolean
|
||||
}
|
||||
Generated
Vendored
+88
@@ -0,0 +1,88 @@
|
||||
import { UnreachableCaseError } from '../../../utils/unreachable-case-error.js'
|
||||
import {
|
||||
computeAbstractModifier,
|
||||
computeAccessibilityModifier,
|
||||
computeAsyncModifier,
|
||||
computeDecoratedModifier,
|
||||
computeOptionalModifier,
|
||||
computeOverrideModifier,
|
||||
computeStaticModifier,
|
||||
} from './common-modifiers.js'
|
||||
import { computeMethodOrPropertyNameDetails } from './compute-method-or-property-name-details.js'
|
||||
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
|
||||
/**
|
||||
* Computes details related to a method.
|
||||
*
|
||||
* @param params - Parameters object.
|
||||
* @param params.isDecorated - Whether the accessor is decorated.
|
||||
* @param params.method - The method node to compute information for.
|
||||
* @param params.sourceCode - The source code object.
|
||||
* @param params.hasParentDeclare - Whether the parent class is a declare class.
|
||||
* @returns An object containing various details about the method.
|
||||
*/
|
||||
function computeMethodDetails({
|
||||
hasParentDeclare,
|
||||
isDecorated,
|
||||
sourceCode,
|
||||
method,
|
||||
}) {
|
||||
let nameDetails = computeMethodOrPropertyNameDetails(method, sourceCode)
|
||||
return {
|
||||
modifiers: computeModifiers({
|
||||
hasPrivateHash: nameDetails.hasPrivateHash,
|
||||
isDecorated,
|
||||
method,
|
||||
}),
|
||||
addSafetySemicolonWhenInline: shouldAddSafetySemicolonWhenInline({
|
||||
hasParentDeclare,
|
||||
method,
|
||||
}),
|
||||
selectors: computeSelectors(method),
|
||||
isStatic: method.static,
|
||||
nameDetails,
|
||||
}
|
||||
}
|
||||
function computeSelectors(method) {
|
||||
return [...computeSetterOrConstructorSelector(), 'method']
|
||||
function computeSetterOrConstructorSelector() {
|
||||
switch (method.kind) {
|
||||
case 'constructor':
|
||||
return ['constructor']
|
||||
case 'method':
|
||||
return []
|
||||
case 'set':
|
||||
return ['set-method']
|
||||
case 'get':
|
||||
return ['get-method']
|
||||
/* v8 ignore next 2 -- @preserve Exhaustive guard. */
|
||||
default:
|
||||
throw new UnreachableCaseError(method.kind)
|
||||
}
|
||||
}
|
||||
}
|
||||
function computeModifiers({ hasPrivateHash, isDecorated, method }) {
|
||||
return [
|
||||
...computeStaticModifier(method),
|
||||
...computeAbstractModifier(method),
|
||||
...computeDecoratedModifier(isDecorated),
|
||||
...computeOverrideModifier(method),
|
||||
...computeAccessibilityModifier({
|
||||
hasPrivateHash,
|
||||
node: method,
|
||||
}),
|
||||
...computeOptionalModifier(method),
|
||||
...computeAsyncModifier(method.value),
|
||||
]
|
||||
}
|
||||
function shouldAddSafetySemicolonWhenInline({ hasParentDeclare, method }) {
|
||||
switch (method.type) {
|
||||
case AST_NODE_TYPES.TSAbstractMethodDefinition:
|
||||
return true
|
||||
case AST_NODE_TYPES.MethodDefinition:
|
||||
return hasParentDeclare
|
||||
/* v8 ignore next 2 -- @preserve Exhaustive guard. */
|
||||
default:
|
||||
throw new UnreachableCaseError(method)
|
||||
}
|
||||
}
|
||||
export { computeMethodDetails }
|
||||
Generated
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
import { TSESTree } from '@typescript-eslint/types'
|
||||
import { TSESLint } from '@typescript-eslint/utils'
|
||||
import { NodeNameDetails } from '../types.js'
|
||||
/**
|
||||
* Computes the name details of a method or property node.
|
||||
*
|
||||
* @param node - The method or property node to compute the name for.
|
||||
* @param sourceCode - The ESLint source code object.
|
||||
* @returns An object containing the name, whether it has a private hash, and
|
||||
* the name without the starting hash.
|
||||
*/
|
||||
export declare function computeMethodOrPropertyNameDetails(
|
||||
node:
|
||||
| TSESTree.TSAbstractPropertyDefinition
|
||||
| TSESTree.TSAbstractMethodDefinition
|
||||
| TSESTree.TSAbstractAccessorProperty
|
||||
| TSESTree.PropertyDefinition
|
||||
| TSESTree.MethodDefinition
|
||||
| TSESTree.AccessorProperty,
|
||||
sourceCode: TSESLint.SourceCode,
|
||||
): NodeNameDetails
|
||||
Generated
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
import { computeIdentifierNameDetails } from '../compute-identifier-name-details.js'
|
||||
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
|
||||
/**
|
||||
* Computes the name details of a method or property node.
|
||||
*
|
||||
* @param node - The method or property node to compute the name for.
|
||||
* @param sourceCode - The ESLint source code object.
|
||||
* @returns An object containing the name, whether it has a private hash, and
|
||||
* the name without the starting hash.
|
||||
*/
|
||||
function computeMethodOrPropertyNameDetails(node, sourceCode) {
|
||||
switch (node.key.type) {
|
||||
case AST_NODE_TYPES.PrivateIdentifier:
|
||||
case AST_NODE_TYPES.Identifier:
|
||||
case AST_NODE_TYPES.Literal:
|
||||
return computeIdentifierNameDetails(node.key)
|
||||
default:
|
||||
return {
|
||||
nameWithoutStartingHash: sourceCode.getText(node.key),
|
||||
name: sourceCode.getText(node.key),
|
||||
hasPrivateHash: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
export { computeMethodOrPropertyNameDetails }
|
||||
Generated
Vendored
+41
@@ -0,0 +1,41 @@
|
||||
import { TSESTree } from '@typescript-eslint/types'
|
||||
import { TSESLint } from '@typescript-eslint/utils'
|
||||
import { NodeNameDetails, Modifier, Selector } from '../types.js'
|
||||
import { RegexOption } from '../../../types/common-options.js'
|
||||
/**
|
||||
* Computes details related to a property.
|
||||
*
|
||||
* @param params - Parameters object.
|
||||
* @param params.isDecorated - Whether the accessor is decorated.
|
||||
* @param params.property - The property node to compute information for.
|
||||
* @param params.ignoreCallbackDependenciesPatterns - Patterns to ignore when
|
||||
* computing dependencies.
|
||||
* @param params.useExperimentalDependencyDetection - Whether to use
|
||||
* experimental dependency detection.
|
||||
* @param params.sourceCode - The source code object.
|
||||
* @param params.className - The name of the class containing the property.
|
||||
* @returns An object containing various details about the property.
|
||||
*/
|
||||
export declare function computePropertyDetails({
|
||||
ignoreCallbackDependenciesPatterns,
|
||||
useExperimentalDependencyDetection,
|
||||
isDecorated,
|
||||
sourceCode,
|
||||
className,
|
||||
property,
|
||||
}: {
|
||||
property: TSESTree.TSAbstractPropertyDefinition | TSESTree.PropertyDefinition
|
||||
ignoreCallbackDependenciesPatterns: RegexOption
|
||||
useExperimentalDependencyDetection: boolean
|
||||
sourceCode: TSESLint.SourceCode
|
||||
className: undefined | string
|
||||
isDecorated: boolean
|
||||
}): {
|
||||
memberValue: undefined | string
|
||||
nameDetails: NodeNameDetails
|
||||
dependencyNames: string[]
|
||||
dependencies: string[]
|
||||
modifiers: Modifier[]
|
||||
selectors: Selector[]
|
||||
isStatic: boolean
|
||||
}
|
||||
Generated
Vendored
+127
@@ -0,0 +1,127 @@
|
||||
import {
|
||||
computeAbstractModifier,
|
||||
computeAccessibilityModifier,
|
||||
computeAsyncModifier,
|
||||
computeDecoratedModifier,
|
||||
computeOptionalModifier,
|
||||
computeOverrideModifier,
|
||||
computeReadonlyModifier,
|
||||
computeStaticModifier,
|
||||
} from './common-modifiers.js'
|
||||
import { computeDependencyName } from '../compute-dependency-name.js'
|
||||
import { computeDependencies } from '../compute-dependencies.js'
|
||||
import { computeMethodOrPropertyNameDetails } from './compute-method-or-property-name-details.js'
|
||||
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
|
||||
/**
|
||||
* Computes details related to a property.
|
||||
*
|
||||
* @param params - Parameters object.
|
||||
* @param params.isDecorated - Whether the accessor is decorated.
|
||||
* @param params.property - The property node to compute information for.
|
||||
* @param params.ignoreCallbackDependenciesPatterns - Patterns to ignore when
|
||||
* computing dependencies.
|
||||
* @param params.useExperimentalDependencyDetection - Whether to use
|
||||
* experimental dependency detection.
|
||||
* @param params.sourceCode - The source code object.
|
||||
* @param params.className - The name of the class containing the property.
|
||||
* @returns An object containing various details about the property.
|
||||
*/
|
||||
function computePropertyDetails({
|
||||
ignoreCallbackDependenciesPatterns,
|
||||
useExperimentalDependencyDetection,
|
||||
isDecorated,
|
||||
sourceCode,
|
||||
className,
|
||||
property,
|
||||
}) {
|
||||
let nameDetails = computeMethodOrPropertyNameDetails(property, sourceCode)
|
||||
let modifiers = computeModifiers({
|
||||
hasPrivateHash: nameDetails.hasPrivateHash,
|
||||
isDecorated,
|
||||
property,
|
||||
})
|
||||
return {
|
||||
dependencyNames: [
|
||||
computeDependencyName({
|
||||
nodeNameWithoutStartingHash: nameDetails.nameWithoutStartingHash,
|
||||
hasPrivateHash: nameDetails.hasPrivateHash,
|
||||
isStatic: modifiers.includes('static'),
|
||||
}),
|
||||
],
|
||||
dependencies: computePropertyDependencies({
|
||||
ignoreCallbackDependenciesPatterns,
|
||||
useExperimentalDependencyDetection,
|
||||
className,
|
||||
property,
|
||||
}),
|
||||
memberValue:
|
||||
!isFunctionExpression(property.value) && property.value ?
|
||||
sourceCode.getText(property.value)
|
||||
: void 0,
|
||||
selectors: computeSelectors(property),
|
||||
isStatic: property.static,
|
||||
nameDetails,
|
||||
modifiers,
|
||||
}
|
||||
}
|
||||
function computeModifiers({ hasPrivateHash, isDecorated, property }) {
|
||||
return [
|
||||
...computeStaticModifier(property),
|
||||
...computeDeclareModifier(),
|
||||
...computeAbstractModifier(property),
|
||||
...computeDecoratedModifier(isDecorated),
|
||||
...computeOverrideModifier(property),
|
||||
...computeReadonlyModifier(property),
|
||||
...computeAccessibilityModifier({
|
||||
hasPrivateHash,
|
||||
node: property,
|
||||
}),
|
||||
...computeOptionalModifier(property),
|
||||
...computeAsyncModifierIfFunctionProperty(),
|
||||
]
|
||||
function computeDeclareModifier() {
|
||||
return property.declare ? ['declare'] : []
|
||||
}
|
||||
function computeAsyncModifierIfFunctionProperty() {
|
||||
if (!isFunctionExpression(property.value)) {
|
||||
return []
|
||||
}
|
||||
return computeAsyncModifier(property.value)
|
||||
}
|
||||
}
|
||||
function computePropertyDependencies({
|
||||
ignoreCallbackDependenciesPatterns,
|
||||
useExperimentalDependencyDetection,
|
||||
className,
|
||||
property,
|
||||
}) {
|
||||
if (isFunctionExpression(property.value)) {
|
||||
return []
|
||||
}
|
||||
if (!property.value) {
|
||||
return []
|
||||
}
|
||||
return computeDependencies({
|
||||
ignoreCallbackDependenciesPatterns,
|
||||
useExperimentalDependencyDetection,
|
||||
isMemberStatic: property.static,
|
||||
expression: property.value,
|
||||
className,
|
||||
})
|
||||
}
|
||||
function computeSelectors(property) {
|
||||
return [...computeFunctionPropertySelector(), 'property']
|
||||
function computeFunctionPropertySelector() {
|
||||
return isFunctionExpression(property.value) ? ['function-property'] : []
|
||||
}
|
||||
}
|
||||
function isFunctionExpression(node) {
|
||||
if (!node) {
|
||||
return false
|
||||
}
|
||||
return (
|
||||
node.type === AST_NODE_TYPES.ArrowFunctionExpression ||
|
||||
node.type === AST_NODE_TYPES.FunctionExpression
|
||||
)
|
||||
}
|
||||
export { computePropertyDetails }
|
||||
Generated
Vendored
+30
@@ -0,0 +1,30 @@
|
||||
import { TSESTree } from '@typescript-eslint/types'
|
||||
import { RegexOption } from '../../../types/common-options.js'
|
||||
import { Modifier, Selector } from '../types.js'
|
||||
/**
|
||||
* Computes details related to a static block.
|
||||
*
|
||||
* @param params - Parameters object.
|
||||
* @param params.staticBlock - The static block node to compute information for.
|
||||
* @param params.ignoreCallbackDependenciesPatterns - Patterns to ignore when
|
||||
* computing dependencies.
|
||||
* @param params.useExperimentalDependencyDetection - Whether to use
|
||||
* experimental dependency detection.
|
||||
* @param params.className - The name of the class containing the property.
|
||||
* @returns An object containing various details about the static block.
|
||||
*/
|
||||
export declare function computeStaticBlockDetails({
|
||||
ignoreCallbackDependenciesPatterns,
|
||||
useExperimentalDependencyDetection,
|
||||
staticBlock,
|
||||
className,
|
||||
}: {
|
||||
ignoreCallbackDependenciesPatterns: RegexOption
|
||||
useExperimentalDependencyDetection: boolean
|
||||
staticBlock: TSESTree.StaticBlock
|
||||
className: undefined | string
|
||||
}): {
|
||||
dependencies: string[]
|
||||
selectors: Selector[]
|
||||
modifiers: Modifier[]
|
||||
}
|
||||
Generated
Vendored
+32
@@ -0,0 +1,32 @@
|
||||
import { computeDependencies } from '../compute-dependencies.js'
|
||||
/**
|
||||
* Computes details related to a static block.
|
||||
*
|
||||
* @param params - Parameters object.
|
||||
* @param params.staticBlock - The static block node to compute information for.
|
||||
* @param params.ignoreCallbackDependenciesPatterns - Patterns to ignore when
|
||||
* computing dependencies.
|
||||
* @param params.useExperimentalDependencyDetection - Whether to use
|
||||
* experimental dependency detection.
|
||||
* @param params.className - The name of the class containing the property.
|
||||
* @returns An object containing various details about the static block.
|
||||
*/
|
||||
function computeStaticBlockDetails({
|
||||
ignoreCallbackDependenciesPatterns,
|
||||
useExperimentalDependencyDetection,
|
||||
staticBlock,
|
||||
className,
|
||||
}) {
|
||||
return {
|
||||
dependencies: computeDependencies({
|
||||
ignoreCallbackDependenciesPatterns,
|
||||
useExperimentalDependencyDetection,
|
||||
expression: staticBlock,
|
||||
isMemberStatic: true,
|
||||
className,
|
||||
}),
|
||||
selectors: ['static-block'],
|
||||
modifiers: [],
|
||||
}
|
||||
}
|
||||
export { computeStaticBlockDetails }
|
||||
Generated
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
import { TSESTree } from '@typescript-eslint/types'
|
||||
import { TSESLint } from '@typescript-eslint/utils'
|
||||
import { MessageId, Options } from './types.js'
|
||||
export declare let defaultOptions: Required<Options[number]>
|
||||
export declare function sortClass({
|
||||
matchedAstSelectors,
|
||||
context,
|
||||
node,
|
||||
}: {
|
||||
context: Readonly<TSESLint.RuleContext<MessageId, Options>>
|
||||
matchedAstSelectors: ReadonlySet<string>
|
||||
node: TSESTree.ClassBody
|
||||
}): void
|
||||
Generated
Vendored
+324
@@ -0,0 +1,324 @@
|
||||
import { UnreachableCaseError } from '../../utils/unreachable-case-error.js'
|
||||
import { getGroupIndex } from '../../utils/get-group-index.js'
|
||||
import { populateSortingNodeGroupsWithDependencies } from '../../utils/populate-sorting-node-groups-with-dependencies.js'
|
||||
import { validateNewlinesAndPartitionConfiguration } from '../../utils/validate-newlines-and-partition-configuration.js'
|
||||
import { defaultComparatorByOptionsComputer } from '../../utils/compare/default-comparator-by-options-computer.js'
|
||||
import { buildOptionsByGroupIndexComputer } from '../../utils/build-options-by-group-index-computer.js'
|
||||
import { validateCustomSortConfiguration } from '../../utils/validate-custom-sort-configuration.js'
|
||||
import { validateGroupsConfiguration } from '../../utils/validate-groups-configuration.js'
|
||||
import { generatePredefinedGroups } from '../../utils/generate-predefined-groups.js'
|
||||
import { sortNodesByDependencies } from '../../utils/sort-nodes-by-dependencies.js'
|
||||
import { getEslintDisabledLines } from '../../utils/get-eslint-disabled-lines.js'
|
||||
import { doesCustomGroupMatch } from '../../utils/does-custom-group-match.js'
|
||||
import { isNodeEslintDisabled } from '../../utils/is-node-eslint-disabled.js'
|
||||
import { sortNodesByGroups } from '../../utils/sort-nodes-by-groups.js'
|
||||
import { reportAllErrors } from '../../utils/report-all-errors.js'
|
||||
import { shouldPartition } from '../../utils/should-partition.js'
|
||||
import { computeGroup } from '../../utils/compute-group.js'
|
||||
import { rangeToDiff } from '../../utils/range-to-diff.js'
|
||||
import { getSettings } from '../../utils/get-settings.js'
|
||||
import { isSortable } from '../../utils/is-sortable.js'
|
||||
import { complete } from '../../utils/complete.js'
|
||||
import { getNodeDecorators } from '../../utils/get-node-decorators.js'
|
||||
import { getDecoratorName } from '../../utils/get-decorator-name.js'
|
||||
import {
|
||||
DEPENDENCY_ORDER_ERROR_ID,
|
||||
EXTRA_SPACING_ERROR_ID,
|
||||
GROUP_ORDER_ERROR_ID,
|
||||
MISSED_SPACING_ERROR_ID,
|
||||
ORDER_ERROR_ID,
|
||||
allModifiers,
|
||||
allSelectors,
|
||||
} from './types.js'
|
||||
import { buildOverloadSignatureNewlinesBetweenValueGetter } from '../../utils/overload-signature/build-overload-signature-newlines-between-value-getter.js'
|
||||
import { populateSortingNodeGroupsWithOverloadSignature } from '../../utils/overload-signature/populate-sorting-node-groups-with-overload-signature.js'
|
||||
import { computeIndexSignatureDetails } from './node-info/compute-index-signature-details.js'
|
||||
import { computeDependenciesBySortingNode } from './compute-dependencies-by-sorting-node.js'
|
||||
import { computeStaticBlockDetails } from './node-info/compute-static-block-details.js'
|
||||
import { computeOverloadSignatureGroups } from './compute-overload-signature-groups.js'
|
||||
import { computeMatchedContextOptions } from './compute-matched-context-options.js'
|
||||
import { computePropertyDetails } from './node-info/compute-property-details.js'
|
||||
import { computeAccessorDetails } from './node-info/compute-accessor-details.js'
|
||||
import { computeMethodDetails } from './node-info/compute-method-details.js'
|
||||
import { isKnownClassElement } from './is-known-class-element.js'
|
||||
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
|
||||
/**
|
||||
* Cache computed groups by modifiers and selectors for performance.
|
||||
*/
|
||||
var cachedGroupsByModifiersAndSelectors = /* @__PURE__ */ new Map()
|
||||
var defaultOptions = {
|
||||
groups: [
|
||||
'index-signature',
|
||||
['static-property', 'static-accessor-property'],
|
||||
['static-get-method', 'static-set-method'],
|
||||
['protected-static-property', 'protected-static-accessor-property'],
|
||||
['protected-static-get-method', 'protected-static-set-method'],
|
||||
['private-static-property', 'private-static-accessor-property'],
|
||||
['private-static-get-method', 'private-static-set-method'],
|
||||
'static-block',
|
||||
['property', 'accessor-property'],
|
||||
['get-method', 'set-method'],
|
||||
['protected-property', 'protected-accessor-property'],
|
||||
['protected-get-method', 'protected-set-method'],
|
||||
['private-property', 'private-accessor-property'],
|
||||
['private-get-method', 'private-set-method'],
|
||||
'constructor',
|
||||
['static-method', 'static-function-property'],
|
||||
['protected-static-method', 'protected-static-function-property'],
|
||||
['private-static-method', 'private-static-function-property'],
|
||||
['method', 'function-property'],
|
||||
['protected-method', 'protected-function-property'],
|
||||
['private-method', 'private-function-property'],
|
||||
'unknown',
|
||||
],
|
||||
useExperimentalDependencyDetection: true,
|
||||
ignoreCallbackDependenciesPatterns: [],
|
||||
newlinesBetweenOverloadSignatures: 0,
|
||||
fallbackSort: { type: 'unsorted' },
|
||||
newlinesInside: 'newlinesBetween',
|
||||
partitionByComment: false,
|
||||
partitionByNewLine: false,
|
||||
newlinesBetween: 'ignore',
|
||||
specialCharacters: 'keep',
|
||||
useConfigurationIf: {},
|
||||
type: 'alphabetical',
|
||||
ignoreCase: true,
|
||||
customGroups: [],
|
||||
locales: 'en-US',
|
||||
alphabet: '',
|
||||
order: 'asc',
|
||||
}
|
||||
function sortClass({ matchedAstSelectors, context, node }) {
|
||||
let classElements = node.body.filter(isKnownClassElement)
|
||||
if (!isSortable(classElements)) {
|
||||
return
|
||||
}
|
||||
let settings = getSettings(context.settings)
|
||||
let options = complete(
|
||||
computeMatchedContextOptions({
|
||||
matchedAstSelectors,
|
||||
classElements,
|
||||
context,
|
||||
}),
|
||||
settings,
|
||||
defaultOptions,
|
||||
)
|
||||
validateCustomSortConfiguration(options)
|
||||
validateGroupsConfiguration({
|
||||
modifiers: allModifiers,
|
||||
selectors: allSelectors,
|
||||
options,
|
||||
})
|
||||
validateNewlinesAndPartitionConfiguration(options)
|
||||
let { sourceCode, id } = context
|
||||
let eslintDisabledLines = getEslintDisabledLines({
|
||||
ruleName: id,
|
||||
sourceCode,
|
||||
})
|
||||
let optionsByGroupIndexComputer = buildOptionsByGroupIndexComputer(options)
|
||||
let overloadSignatureNewlinesBetweenValueGetter =
|
||||
buildOverloadSignatureNewlinesBetweenValueGetter(
|
||||
options.newlinesBetweenOverloadSignatures,
|
||||
)
|
||||
let className = node.parent.id?.name
|
||||
let sortingNodeGroupsWithoutOverloadSignature = classElements.reduce(
|
||||
(accumulator, member) => {
|
||||
let dependencies = []
|
||||
let isDecorated = false
|
||||
let decorators = []
|
||||
if ('decorators' in member) {
|
||||
decorators = getNodeDecorators(member).map(decorator =>
|
||||
getDecoratorName({
|
||||
sourceCode,
|
||||
decorator,
|
||||
}),
|
||||
)
|
||||
isDecorated = decorators.length > 0
|
||||
}
|
||||
let addSafetySemicolonWhenInline
|
||||
let dependencyNames
|
||||
let name
|
||||
let nameDetails
|
||||
let memberValue
|
||||
let isStatic
|
||||
let modifiers
|
||||
let selectors
|
||||
switch (member.type) {
|
||||
case AST_NODE_TYPES.TSAbstractPropertyDefinition:
|
||||
case AST_NODE_TYPES.PropertyDefinition:
|
||||
addSafetySemicolonWhenInline = true
|
||||
;({
|
||||
dependencyNames,
|
||||
dependencies,
|
||||
memberValue,
|
||||
nameDetails,
|
||||
modifiers,
|
||||
selectors,
|
||||
isStatic,
|
||||
} = computePropertyDetails({
|
||||
ignoreCallbackDependenciesPatterns:
|
||||
options.ignoreCallbackDependenciesPatterns,
|
||||
useExperimentalDependencyDetection:
|
||||
options.useExperimentalDependencyDetection,
|
||||
property: member,
|
||||
isDecorated,
|
||||
sourceCode,
|
||||
className,
|
||||
}))
|
||||
;({ name } = nameDetails)
|
||||
break
|
||||
case AST_NODE_TYPES.TSAbstractMethodDefinition:
|
||||
case AST_NODE_TYPES.MethodDefinition:
|
||||
dependencyNames = []
|
||||
;({
|
||||
addSafetySemicolonWhenInline,
|
||||
nameDetails,
|
||||
selectors,
|
||||
modifiers,
|
||||
isStatic,
|
||||
} = computeMethodDetails({
|
||||
hasParentDeclare: node.parent.declare,
|
||||
method: member,
|
||||
isDecorated,
|
||||
sourceCode,
|
||||
}))
|
||||
;({ name } = nameDetails)
|
||||
break
|
||||
case AST_NODE_TYPES.TSAbstractAccessorProperty:
|
||||
case AST_NODE_TYPES.AccessorProperty:
|
||||
addSafetySemicolonWhenInline = true
|
||||
;({ dependencyNames, nameDetails, selectors, modifiers, isStatic } =
|
||||
computeAccessorDetails({
|
||||
accessor: member,
|
||||
isDecorated,
|
||||
sourceCode,
|
||||
}))
|
||||
;({ name } = nameDetails)
|
||||
break
|
||||
case AST_NODE_TYPES.TSIndexSignature:
|
||||
addSafetySemicolonWhenInline = true
|
||||
dependencyNames = []
|
||||
nameDetails = null
|
||||
isStatic = false
|
||||
;({ modifiers, selectors, name } = computeIndexSignatureDetails({
|
||||
indexSignature: member,
|
||||
sourceCode,
|
||||
}))
|
||||
break
|
||||
case AST_NODE_TYPES.StaticBlock:
|
||||
addSafetySemicolonWhenInline = false
|
||||
dependencyNames = []
|
||||
name = 'static'
|
||||
nameDetails = null
|
||||
isStatic = true
|
||||
;({ dependencies, selectors, modifiers } = computeStaticBlockDetails({
|
||||
useExperimentalDependencyDetection:
|
||||
options.useExperimentalDependencyDetection,
|
||||
ignoreCallbackDependenciesPatterns:
|
||||
options.ignoreCallbackDependenciesPatterns,
|
||||
staticBlock: member,
|
||||
className,
|
||||
}))
|
||||
break
|
||||
/* v8 ignore next 2 -- @preserve Exhaustive guard. */
|
||||
default:
|
||||
throw new UnreachableCaseError(member)
|
||||
}
|
||||
let group = computeGroup({
|
||||
customGroupMatcher: customGroup =>
|
||||
doesCustomGroupMatch({
|
||||
elementValue: memberValue,
|
||||
elementName: name,
|
||||
customGroup,
|
||||
decorators,
|
||||
modifiers,
|
||||
selectors,
|
||||
}),
|
||||
predefinedGroups: generatePredefinedGroups({
|
||||
cache: cachedGroupsByModifiersAndSelectors,
|
||||
selectors,
|
||||
modifiers,
|
||||
}),
|
||||
options,
|
||||
})
|
||||
let sortingNode = {
|
||||
isEslintDisabled: isNodeEslintDisabled(member, eslintDisabledLines),
|
||||
size: rangeToDiff(member, sourceCode),
|
||||
addSafetySemicolonWhenInline,
|
||||
dependencyNames,
|
||||
node: member,
|
||||
dependencies,
|
||||
nameDetails,
|
||||
isStatic,
|
||||
group,
|
||||
name,
|
||||
}
|
||||
let lastSortingNode = accumulator.at(-1)?.at(-1)
|
||||
if (
|
||||
shouldPartition({
|
||||
lastSortingNode,
|
||||
sortingNode,
|
||||
sourceCode,
|
||||
options,
|
||||
})
|
||||
) {
|
||||
accumulator.push([])
|
||||
}
|
||||
accumulator.at(-1).push({
|
||||
...sortingNode,
|
||||
partitionId: accumulator.length,
|
||||
})
|
||||
return accumulator
|
||||
},
|
||||
[[]],
|
||||
)
|
||||
let sortingNodeGroups = populateSortingNodeGroupsWithOverloadSignature({
|
||||
overloadSignatureGroups: computeOverloadSignatureGroups(classElements),
|
||||
sortingNodeGroups: sortingNodeGroupsWithoutOverloadSignature,
|
||||
})
|
||||
if (options.useExperimentalDependencyDetection) {
|
||||
sortingNodeGroups = populateSortingNodeGroupsWithDependencies({
|
||||
dependenciesBySortingNode: computeDependenciesBySortingNode({
|
||||
ignoreCallbackDependenciesPatterns:
|
||||
options.ignoreCallbackDependenciesPatterns,
|
||||
sortingNodes: sortingNodeGroups.flat(),
|
||||
classBody: node,
|
||||
sourceCode,
|
||||
}),
|
||||
sortingNodeGroups,
|
||||
})
|
||||
}
|
||||
let sortingNodes = sortingNodeGroups.flat()
|
||||
reportAllErrors({
|
||||
availableMessageIds: {
|
||||
missedSpacingBetweenMembers: MISSED_SPACING_ERROR_ID,
|
||||
unexpectedDependencyOrder: DEPENDENCY_ORDER_ERROR_ID,
|
||||
extraSpacingBetweenMembers: EXTRA_SPACING_ERROR_ID,
|
||||
unexpectedGroupOrder: GROUP_ORDER_ERROR_ID,
|
||||
unexpectedOrder: ORDER_ERROR_ID,
|
||||
},
|
||||
newlinesBetweenValueGetter: overloadSignatureNewlinesBetweenValueGetter,
|
||||
sortNodesExcludingEslintDisabled,
|
||||
nodes: sortingNodes,
|
||||
options,
|
||||
context,
|
||||
})
|
||||
function sortNodesExcludingEslintDisabled(ignoreEslintDisabledNodes) {
|
||||
return sortNodesByDependencies(
|
||||
sortingNodeGroups.flatMap(sortingNodeGroup =>
|
||||
sortNodesByGroups({
|
||||
isNodeIgnored: sortingNode =>
|
||||
getGroupIndex(options.groups, sortingNode) ===
|
||||
options.groups.length,
|
||||
comparatorByOptionsComputer: defaultComparatorByOptionsComputer,
|
||||
optionsByGroupIndexComputer,
|
||||
ignoreEslintDisabledNodes,
|
||||
nodes: sortingNodeGroup,
|
||||
groups: options.groups,
|
||||
}),
|
||||
),
|
||||
{ ignoreEslintDisabledNodes },
|
||||
)
|
||||
}
|
||||
}
|
||||
export { defaultOptions, sortClass }
|
||||
Generated
Vendored
+155
@@ -0,0 +1,155 @@
|
||||
import { JSONSchema4 } from '@typescript-eslint/utils/json-schema'
|
||||
import { TSESTree } from '@typescript-eslint/types'
|
||||
import { SortingNodeWithDependencies } from '../../utils/sort-nodes-by-dependencies.js'
|
||||
import { NewlinesBetweenOption } from '../../types/common-groups-options.js'
|
||||
import { RegexOption, TypeOption } from '../../types/common-options.js'
|
||||
import { AllCommonOptions } from '../../types/all-common-options.js'
|
||||
export type MessageId =
|
||||
| typeof DEPENDENCY_ORDER_ERROR_ID
|
||||
| typeof MISSED_SPACING_ERROR_ID
|
||||
| typeof EXTRA_SPACING_ERROR_ID
|
||||
| typeof GROUP_ORDER_ERROR_ID
|
||||
| typeof ORDER_ERROR_ID
|
||||
export declare const ORDER_ERROR_ID = 'unexpectedClassesOrder'
|
||||
export declare const GROUP_ORDER_ERROR_ID = 'unexpectedClassesGroupOrder'
|
||||
export declare const EXTRA_SPACING_ERROR_ID = 'extraSpacingBetweenClassMembers'
|
||||
export declare const MISSED_SPACING_ERROR_ID =
|
||||
'missedSpacingBetweenClassMembers'
|
||||
export declare const DEPENDENCY_ORDER_ERROR_ID =
|
||||
'unexpectedClassesDependencyOrder'
|
||||
/**
|
||||
* Configuration options for the sort-classes rule.
|
||||
*
|
||||
* This rule enforces consistent ordering of class members (properties, methods,
|
||||
* constructors, etc.) to improve code readability and maintainability.
|
||||
*/
|
||||
export type Options = Partial<
|
||||
{
|
||||
/**
|
||||
* Conditional configuration based on pattern matching.
|
||||
*/
|
||||
useConfigurationIf: {
|
||||
/**
|
||||
* Regular expression pattern to match against all class element names.
|
||||
*/
|
||||
allNamesMatchPattern?: RegexOption
|
||||
/**
|
||||
* AST selector to match against ClassBody nodes.
|
||||
*/
|
||||
matchesAstSelector?: string
|
||||
}
|
||||
/**
|
||||
* Determines how many newlines should be placed between overload signatures
|
||||
* of the same method.
|
||||
*/
|
||||
newlinesBetweenOverloadSignatures: NewlinesBetweenOption
|
||||
/**
|
||||
* Regex patterns for function names whose callback argument dependencies
|
||||
* are ignored during class-member sorting. Dependencies inside these
|
||||
* callbacks won't influence the ordering.
|
||||
*/
|
||||
ignoreCallbackDependenciesPatterns: RegexOption
|
||||
/**
|
||||
* Enables experimental dependency detection.
|
||||
*/
|
||||
useExperimentalDependencyDetection: boolean
|
||||
} & AllCommonOptions<
|
||||
TypeOption,
|
||||
AdditionalSortOptions,
|
||||
CustomGroupMatchOptions
|
||||
>
|
||||
>[]
|
||||
export interface SortClassesSortingNode extends SortingNodeWithDependencies<TSESTree.ClassElement> {
|
||||
overloadSignatureImplementation:
|
||||
| TSESTree.TSAbstractMethodDefinition
|
||||
| TSESTree.MethodDefinition
|
||||
| null
|
||||
nameDetails: NodeNameDetails | null
|
||||
isStatic: boolean
|
||||
}
|
||||
export interface NodeNameDetails {
|
||||
nameWithoutStartingHash: string
|
||||
hasPrivateHash: boolean
|
||||
name: string
|
||||
}
|
||||
/**
|
||||
* Union type of all available class member selectors. Used to identify and
|
||||
* categorize different types of class members.
|
||||
*/
|
||||
export type Selector = (typeof allSelectors)[number]
|
||||
/**
|
||||
* Union type of all available class member modifiers. Includes access
|
||||
* modifiers, async, static, abstract, and other TypeScript modifiers.
|
||||
*/
|
||||
export type Modifier = (typeof allModifiers)[number]
|
||||
/**
|
||||
* Defines a custom group configuration for class members.
|
||||
*
|
||||
* Allows categorizing class members based on their selector type (method,
|
||||
* property, etc.) and various patterns matching their names, values, or
|
||||
* decorators.
|
||||
*/
|
||||
interface CustomGroupMatchOptions {
|
||||
/**
|
||||
* Pattern to match decorator names (e.g., '@Component').
|
||||
*/
|
||||
decoratorNamePattern?: RegexOption
|
||||
/**
|
||||
* Pattern to match the value of the member (for properties with
|
||||
* initializers).
|
||||
*/
|
||||
elementValuePattern?: RegexOption
|
||||
/**
|
||||
* List of modifiers that members must have to be included in this group.
|
||||
*/
|
||||
modifiers?: Modifier[]
|
||||
/**
|
||||
* The type of class member this group applies to.
|
||||
*/
|
||||
selector?: Selector
|
||||
}
|
||||
type AdditionalSortOptions = object
|
||||
/**
|
||||
* Complete list of available class member selectors. Used for validation and
|
||||
* JSON schema generation.
|
||||
*/
|
||||
export declare let allSelectors: readonly [
|
||||
'accessor-property',
|
||||
'index-signature',
|
||||
'constructor',
|
||||
'static-block',
|
||||
'get-method',
|
||||
'set-method',
|
||||
'function-property',
|
||||
'property',
|
||||
'method',
|
||||
]
|
||||
/**
|
||||
* Complete list of available class member modifiers. Used for validation and
|
||||
* JSON schema generation.
|
||||
*/
|
||||
export declare let allModifiers: readonly [
|
||||
'async',
|
||||
'protected',
|
||||
'private',
|
||||
'public',
|
||||
'static',
|
||||
'abstract',
|
||||
'override',
|
||||
'readonly',
|
||||
'decorated',
|
||||
'declare',
|
||||
'optional',
|
||||
]
|
||||
/**
|
||||
* Additional custom group match options JSON schema. Used by ESLint to validate
|
||||
* rule options at configuration time.
|
||||
*
|
||||
* Note: Ideally, we should generate as many schemas as there are selectors, and
|
||||
* ensure that users do not enter invalid modifiers for a given selector.
|
||||
*/
|
||||
export declare let additionalCustomGroupMatchOptionsJsonSchema: Record<
|
||||
string,
|
||||
JSONSchema4
|
||||
>
|
||||
export {}
|
||||
Generated
Vendored
+65
@@ -0,0 +1,65 @@
|
||||
import { buildRegexJsonSchema } from '../../utils/json-schemas/common-json-schemas.js'
|
||||
import {
|
||||
buildCustomGroupModifiersJsonSchema,
|
||||
buildCustomGroupSelectorJsonSchema,
|
||||
} from '../../utils/json-schemas/common-groups-json-schemas.js'
|
||||
var ORDER_ERROR_ID = 'unexpectedClassesOrder'
|
||||
var GROUP_ORDER_ERROR_ID = 'unexpectedClassesGroupOrder'
|
||||
var EXTRA_SPACING_ERROR_ID = 'extraSpacingBetweenClassMembers'
|
||||
var MISSED_SPACING_ERROR_ID = 'missedSpacingBetweenClassMembers'
|
||||
var DEPENDENCY_ORDER_ERROR_ID = 'unexpectedClassesDependencyOrder'
|
||||
/**
|
||||
* Complete list of available class member selectors. Used for validation and
|
||||
* JSON schema generation.
|
||||
*/
|
||||
var allSelectors = [
|
||||
'accessor-property',
|
||||
'index-signature',
|
||||
'constructor',
|
||||
'static-block',
|
||||
'get-method',
|
||||
'set-method',
|
||||
'function-property',
|
||||
'property',
|
||||
'method',
|
||||
]
|
||||
/**
|
||||
* Complete list of available class member modifiers. Used for validation and
|
||||
* JSON schema generation.
|
||||
*/
|
||||
var allModifiers = [
|
||||
'async',
|
||||
'protected',
|
||||
'private',
|
||||
'public',
|
||||
'static',
|
||||
'abstract',
|
||||
'override',
|
||||
'readonly',
|
||||
'decorated',
|
||||
'declare',
|
||||
'optional',
|
||||
]
|
||||
/**
|
||||
* Additional custom group match options JSON schema. Used by ESLint to validate
|
||||
* rule options at configuration time.
|
||||
*
|
||||
* Note: Ideally, we should generate as many schemas as there are selectors, and
|
||||
* ensure that users do not enter invalid modifiers for a given selector.
|
||||
*/
|
||||
var additionalCustomGroupMatchOptionsJsonSchema = {
|
||||
modifiers: buildCustomGroupModifiersJsonSchema(allModifiers),
|
||||
selector: buildCustomGroupSelectorJsonSchema(allSelectors),
|
||||
decoratorNamePattern: buildRegexJsonSchema(),
|
||||
elementValuePattern: buildRegexJsonSchema(),
|
||||
}
|
||||
export {
|
||||
DEPENDENCY_ORDER_ERROR_ID,
|
||||
EXTRA_SPACING_ERROR_ID,
|
||||
GROUP_ORDER_ERROR_ID,
|
||||
MISSED_SPACING_ERROR_ID,
|
||||
ORDER_ERROR_ID,
|
||||
additionalCustomGroupMatchOptionsJsonSchema,
|
||||
allModifiers,
|
||||
allSelectors,
|
||||
}
|
||||
Generated
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
import { Options } from './sort-decorators/types.js'
|
||||
declare const ORDER_ERROR_ID = 'unexpectedDecoratorsOrder'
|
||||
declare const GROUP_ORDER_ERROR_ID = 'unexpectedDecoratorsGroupOrder'
|
||||
declare const EXTRA_SPACING_ERROR_ID = 'extraSpacingBetweenDecorators'
|
||||
declare const MISSED_SPACING_ERROR_ID = 'missedSpacingBetweenDecorators'
|
||||
type MessageId =
|
||||
| typeof MISSED_SPACING_ERROR_ID
|
||||
| typeof EXTRA_SPACING_ERROR_ID
|
||||
| typeof GROUP_ORDER_ERROR_ID
|
||||
| typeof ORDER_ERROR_ID
|
||||
declare const _default: import('@typescript-eslint/utils/ts-eslint').RuleModule<
|
||||
MessageId,
|
||||
Options,
|
||||
{
|
||||
recommended?: boolean
|
||||
},
|
||||
import('@typescript-eslint/utils/ts-eslint').RuleListener
|
||||
> & {
|
||||
name: string
|
||||
}
|
||||
export default _default
|
||||
+261
@@ -0,0 +1,261 @@
|
||||
import { buildCommonJsonSchemas } from '../utils/json-schemas/common-json-schemas.js'
|
||||
import { buildCommonGroupsJsonSchemas } from '../utils/json-schemas/common-groups-json-schemas.js'
|
||||
import {
|
||||
EXTRA_SPACING_ERROR,
|
||||
GROUP_ORDER_ERROR,
|
||||
MISSED_SPACING_ERROR,
|
||||
ORDER_ERROR,
|
||||
} from '../utils/report-errors.js'
|
||||
import {
|
||||
partitionByCommentJsonSchema,
|
||||
partitionByNewLineJsonSchema,
|
||||
} from '../utils/json-schemas/common-partition-json-schemas.js'
|
||||
import { validateNewlinesAndPartitionConfiguration } from '../utils/validate-newlines-and-partition-configuration.js'
|
||||
import { defaultComparatorByOptionsComputer } from '../utils/compare/default-comparator-by-options-computer.js'
|
||||
import { buildOptionsByGroupIndexComputer } from '../utils/build-options-by-group-index-computer.js'
|
||||
import { validateCustomSortConfiguration } from '../utils/validate-custom-sort-configuration.js'
|
||||
import { validateGroupsConfiguration } from '../utils/validate-groups-configuration.js'
|
||||
import { getEslintDisabledLines } from '../utils/get-eslint-disabled-lines.js'
|
||||
import { doesCustomGroupMatch } from '../utils/does-custom-group-match.js'
|
||||
import { isNodeEslintDisabled } from '../utils/is-node-eslint-disabled.js'
|
||||
import { sortNodesByGroups } from '../utils/sort-nodes-by-groups.js'
|
||||
import { reportAllErrors } from '../utils/report-all-errors.js'
|
||||
import { shouldPartition } from '../utils/should-partition.js'
|
||||
import { computeGroup } from '../utils/compute-group.js'
|
||||
import { rangeToDiff } from '../utils/range-to-diff.js'
|
||||
import { getSettings } from '../utils/get-settings.js'
|
||||
import { isSortable } from '../utils/is-sortable.js'
|
||||
import { complete } from '../utils/complete.js'
|
||||
import { createEslintRule } from '../utils/create-eslint-rule.js'
|
||||
import { getNodeDecorators } from '../utils/get-node-decorators.js'
|
||||
import { getDecoratorName } from '../utils/get-decorator-name.js'
|
||||
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
|
||||
var ORDER_ERROR_ID = 'unexpectedDecoratorsOrder'
|
||||
var GROUP_ORDER_ERROR_ID = 'unexpectedDecoratorsGroupOrder'
|
||||
var EXTRA_SPACING_ERROR_ID = 'extraSpacingBetweenDecorators'
|
||||
var MISSED_SPACING_ERROR_ID = 'missedSpacingBetweenDecorators'
|
||||
var defaultOptions = {
|
||||
fallbackSort: { type: 'unsorted' },
|
||||
newlinesInside: 'newlinesBetween',
|
||||
specialCharacters: 'keep',
|
||||
partitionByComment: false,
|
||||
partitionByNewLine: false,
|
||||
newlinesBetween: 'ignore',
|
||||
sortOnProperties: true,
|
||||
sortOnParameters: true,
|
||||
sortOnAccessors: true,
|
||||
type: 'alphabetical',
|
||||
sortOnClasses: true,
|
||||
sortOnMethods: true,
|
||||
ignoreCase: true,
|
||||
customGroups: [],
|
||||
locales: 'en-US',
|
||||
alphabet: '',
|
||||
order: 'asc',
|
||||
groups: [],
|
||||
}
|
||||
var sort_decorators_default = createEslintRule({
|
||||
meta: {
|
||||
schema: {
|
||||
items: {
|
||||
properties: {
|
||||
...buildCommonJsonSchemas(),
|
||||
...buildCommonGroupsJsonSchemas(),
|
||||
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: partitionByCommentJsonSchema,
|
||||
partitionByNewLine: partitionByNewLineJsonSchema,
|
||||
},
|
||||
additionalProperties: false,
|
||||
type: 'object',
|
||||
},
|
||||
uniqueItems: true,
|
||||
type: 'array',
|
||||
},
|
||||
messages: {
|
||||
[MISSED_SPACING_ERROR_ID]: MISSED_SPACING_ERROR,
|
||||
[EXTRA_SPACING_ERROR_ID]: EXTRA_SPACING_ERROR,
|
||||
[GROUP_ORDER_ERROR_ID]: GROUP_ORDER_ERROR,
|
||||
[ORDER_ERROR_ID]: ORDER_ERROR,
|
||||
},
|
||||
docs: {
|
||||
url: 'https://perfectionist.dev/rules/sort-decorators',
|
||||
description: 'Enforce sorted decorators.',
|
||||
recommended: true,
|
||||
},
|
||||
type: 'suggestion',
|
||||
fixable: 'code',
|
||||
},
|
||||
create: context => {
|
||||
let settings = getSettings(context.settings)
|
||||
let options = complete(context.options.at(0), settings, defaultOptions)
|
||||
validateCustomSortConfiguration(options)
|
||||
validateGroupsConfiguration({
|
||||
modifiers: [],
|
||||
selectors: [],
|
||||
options,
|
||||
})
|
||||
validateNewlinesAndPartitionConfiguration(options)
|
||||
return {
|
||||
Decorator: decorator => {
|
||||
if (!options.sortOnParameters) {
|
||||
return
|
||||
}
|
||||
if (
|
||||
'decorators' in decorator.parent &&
|
||||
decorator.parent.type === AST_NODE_TYPES.Identifier &&
|
||||
decorator.parent.parent.type === AST_NODE_TYPES.FunctionExpression
|
||||
) {
|
||||
let { decorators } = decorator.parent
|
||||
if (decorator !== decorators[0]) {
|
||||
return
|
||||
}
|
||||
sortDecorators(context, options, decorators)
|
||||
}
|
||||
},
|
||||
PropertyDefinition: propertyDefinition => {
|
||||
if (options.sortOnProperties) {
|
||||
sortDecorators(
|
||||
context,
|
||||
options,
|
||||
getNodeDecorators(propertyDefinition),
|
||||
)
|
||||
}
|
||||
},
|
||||
AccessorProperty: accessorDefinition => {
|
||||
if (options.sortOnAccessors) {
|
||||
sortDecorators(
|
||||
context,
|
||||
options,
|
||||
getNodeDecorators(accessorDefinition),
|
||||
)
|
||||
}
|
||||
},
|
||||
MethodDefinition: methodDefinition => {
|
||||
if (options.sortOnMethods) {
|
||||
sortDecorators(context, options, getNodeDecorators(methodDefinition))
|
||||
}
|
||||
},
|
||||
ClassDeclaration: declaration => {
|
||||
if (options.sortOnClasses) {
|
||||
sortDecorators(context, options, getNodeDecorators(declaration))
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
defaultOptions: [defaultOptions],
|
||||
name: 'sort-decorators',
|
||||
})
|
||||
/**
|
||||
* Sorts decorators attached to a class, method, or property.
|
||||
*
|
||||
* Processes the decorators, groups them according to options, and reports any
|
||||
* ordering errors found. Handles partitioning by comments and newlines.
|
||||
*
|
||||
* @param context - The ESLint rule context.
|
||||
* @param options - The sorting options for decorators.
|
||||
* @param decorators - Array of decorator nodes to sort.
|
||||
*/
|
||||
function sortDecorators(context, options, decorators) {
|
||||
if (!isSortable(decorators)) {
|
||||
return
|
||||
}
|
||||
let { sourceCode, id } = context
|
||||
let eslintDisabledLines = getEslintDisabledLines({
|
||||
ruleName: id,
|
||||
sourceCode,
|
||||
})
|
||||
let optionsByGroupIndexComputer = buildOptionsByGroupIndexComputer(options)
|
||||
let formattedMembers = decorators.reduce(
|
||||
(accumulator, decorator) => {
|
||||
let name = getDecoratorName({
|
||||
sourceCode,
|
||||
decorator,
|
||||
})
|
||||
let group = computeGroup({
|
||||
customGroupMatcher: customGroup =>
|
||||
doesCustomGroupMatch({
|
||||
elementName: name,
|
||||
selectors: [],
|
||||
modifiers: [],
|
||||
customGroup,
|
||||
}),
|
||||
predefinedGroups: [],
|
||||
options,
|
||||
})
|
||||
let sortingNode = {
|
||||
isEslintDisabled: isNodeEslintDisabled(decorator, eslintDisabledLines),
|
||||
size: rangeToDiff(decorator, sourceCode),
|
||||
node: decorator,
|
||||
group,
|
||||
name,
|
||||
}
|
||||
let lastSortingNode = accumulator.at(-1)?.at(-1)
|
||||
if (
|
||||
shouldPartition({
|
||||
lastSortingNode,
|
||||
sortingNode,
|
||||
sourceCode,
|
||||
options,
|
||||
})
|
||||
) {
|
||||
accumulator.push([])
|
||||
}
|
||||
accumulator.at(-1).push({
|
||||
...sortingNode,
|
||||
partitionId: accumulator.length,
|
||||
})
|
||||
return accumulator
|
||||
},
|
||||
[[]],
|
||||
)
|
||||
function sortNodesExcludingEslintDisabled(ignoreEslintDisabledNodes) {
|
||||
return formattedMembers.flatMap(nodes =>
|
||||
sortNodesByGroups({
|
||||
comparatorByOptionsComputer: defaultComparatorByOptionsComputer,
|
||||
optionsByGroupIndexComputer,
|
||||
ignoreEslintDisabledNodes,
|
||||
groups: options.groups,
|
||||
nodes,
|
||||
}),
|
||||
)
|
||||
}
|
||||
let nodes = formattedMembers.flat()
|
||||
reportAllErrors({
|
||||
availableMessageIds: {
|
||||
missedSpacingBetweenMembers: MISSED_SPACING_ERROR_ID,
|
||||
extraSpacingBetweenMembers: EXTRA_SPACING_ERROR_ID,
|
||||
unexpectedGroupOrder: GROUP_ORDER_ERROR_ID,
|
||||
unexpectedOrder: ORDER_ERROR_ID,
|
||||
},
|
||||
ignoreFirstNodeHighestBlockComment: true,
|
||||
sortNodesExcludingEslintDisabled,
|
||||
options,
|
||||
context,
|
||||
nodes,
|
||||
})
|
||||
}
|
||||
export { sort_decorators_default as default }
|
||||
Generated
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
import { TSESTree } from '@typescript-eslint/types'
|
||||
import { AllCommonOptions } from '../../types/all-common-options.js'
|
||||
import { TypeOption } from '../../types/common-options.js'
|
||||
import { SortingNode } from '../../types/sorting-node.js'
|
||||
export type Options = Partial<
|
||||
{
|
||||
sortOnParameters: boolean
|
||||
sortOnProperties: boolean
|
||||
sortOnAccessors: boolean
|
||||
sortOnMethods: boolean
|
||||
sortOnClasses: boolean
|
||||
} & AllCommonOptions<
|
||||
TypeOption,
|
||||
AdditionalSortOptions,
|
||||
CustomGroupMatchOptions
|
||||
>
|
||||
>[]
|
||||
export type SortDecoratorsSortingNode = SortingNode<TSESTree.Decorator>
|
||||
/**
|
||||
* Match options for a custom group.
|
||||
*/
|
||||
type CustomGroupMatchOptions = object
|
||||
type AdditionalSortOptions = object
|
||||
export {}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { MessageId, Options } from './sort-enums/types.js'
|
||||
declare const _default: import('@typescript-eslint/utils/ts-eslint').RuleModule<
|
||||
MessageId,
|
||||
Options,
|
||||
{
|
||||
recommended?: boolean
|
||||
},
|
||||
import('@typescript-eslint/utils/ts-eslint').RuleListener
|
||||
> & {
|
||||
name: string
|
||||
}
|
||||
export default _default
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
import {
|
||||
buildCommonJsonSchemas,
|
||||
buildUseConfigurationIfJsonSchema,
|
||||
matchesAstSelectorJsonSchema,
|
||||
useExperimentalDependencyDetectionJsonSchema,
|
||||
} from '../utils/json-schemas/common-json-schemas.js'
|
||||
import { buildCommonGroupsJsonSchemas } from '../utils/json-schemas/common-groups-json-schemas.js'
|
||||
import {
|
||||
DEPENDENCY_ORDER_ERROR,
|
||||
EXTRA_SPACING_ERROR,
|
||||
GROUP_ORDER_ERROR,
|
||||
MISSED_SPACING_ERROR,
|
||||
ORDER_ERROR,
|
||||
} from '../utils/report-errors.js'
|
||||
import {
|
||||
partitionByCommentJsonSchema,
|
||||
partitionByNewLineJsonSchema,
|
||||
} from '../utils/json-schemas/common-partition-json-schemas.js'
|
||||
import { buildAstListeners } from '../utils/build-ast-listeners.js'
|
||||
import { createEslintRule } from '../utils/create-eslint-rule.js'
|
||||
import {
|
||||
DEPENDENCY_ORDER_ERROR_ID,
|
||||
EXTRA_SPACING_ERROR_ID,
|
||||
GROUP_ORDER_ERROR_ID,
|
||||
MISSED_SPACING_ERROR_ID,
|
||||
ORDER_ERROR_ID,
|
||||
additionalCustomGroupMatchOptionsJsonSchema,
|
||||
} from './sort-enums/types.js'
|
||||
import { defaultOptions, sortEnum } from './sort-enums/sort-enum.js'
|
||||
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
|
||||
var sort_enums_default = createEslintRule({
|
||||
meta: {
|
||||
schema: {
|
||||
items: {
|
||||
properties: {
|
||||
...buildCommonJsonSchemas(),
|
||||
...buildCommonGroupsJsonSchemas({
|
||||
additionalCustomGroupMatchProperties:
|
||||
additionalCustomGroupMatchOptionsJsonSchema,
|
||||
}),
|
||||
useConfigurationIf: buildUseConfigurationIfJsonSchema({
|
||||
additionalProperties: {
|
||||
matchesAstSelector: matchesAstSelectorJsonSchema,
|
||||
},
|
||||
}),
|
||||
sortByValue: {
|
||||
description: 'Specifies whether to sort enums by value.',
|
||||
enum: ['always', 'ifNumericEnum', 'never'],
|
||||
type: 'string',
|
||||
},
|
||||
useExperimentalDependencyDetection:
|
||||
useExperimentalDependencyDetectionJsonSchema,
|
||||
partitionByComment: partitionByCommentJsonSchema,
|
||||
partitionByNewLine: partitionByNewLineJsonSchema,
|
||||
},
|
||||
additionalProperties: false,
|
||||
type: 'object',
|
||||
},
|
||||
uniqueItems: true,
|
||||
type: 'array',
|
||||
},
|
||||
messages: {
|
||||
[DEPENDENCY_ORDER_ERROR_ID]: DEPENDENCY_ORDER_ERROR,
|
||||
[MISSED_SPACING_ERROR_ID]: MISSED_SPACING_ERROR,
|
||||
[EXTRA_SPACING_ERROR_ID]: EXTRA_SPACING_ERROR,
|
||||
[GROUP_ORDER_ERROR_ID]: GROUP_ORDER_ERROR,
|
||||
[ORDER_ERROR_ID]: ORDER_ERROR,
|
||||
},
|
||||
docs: {
|
||||
url: 'https://perfectionist.dev/rules/sort-enums',
|
||||
description: 'Enforce sorted TypeScript enums.',
|
||||
recommended: true,
|
||||
},
|
||||
type: 'suggestion',
|
||||
fixable: 'code',
|
||||
},
|
||||
create: context =>
|
||||
buildAstListeners({
|
||||
nodeTypes: [AST_NODE_TYPES.TSEnumDeclaration],
|
||||
sorter: sortEnum,
|
||||
context,
|
||||
}),
|
||||
defaultOptions: [defaultOptions],
|
||||
name: 'sort-enums',
|
||||
})
|
||||
export { sort_enums_default as default }
|
||||
Generated
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
import { ComparatorByOptionsComputer } from '../../utils/compare/default-comparator-by-options-computer.js'
|
||||
import { SortEnumsSortingNode, Options } from './types.js'
|
||||
/**
|
||||
* Builds a comparator computer function for sorting enum members.
|
||||
*
|
||||
* Creates a function that returns the appropriate comparator based on the
|
||||
* sorting options and whether the enum is numeric. Handles sorting by name or
|
||||
* by value depending on the `sortByValue` option.
|
||||
*
|
||||
* @param isNumericEnum - Whether the enum contains only numeric values.
|
||||
* @returns A comparator computer function that creates comparators from
|
||||
* options.
|
||||
*/
|
||||
export declare function buildComparatorByOptionsComputer(
|
||||
isNumericEnum: boolean,
|
||||
): ComparatorByOptionsComputer<Required<Options[number]>, SortEnumsSortingNode>
|
||||
Generated
Vendored
+84
@@ -0,0 +1,84 @@
|
||||
import { UnreachableCaseError } from '../../utils/unreachable-case-error.js'
|
||||
import { buildLineLengthComparator } from '../../utils/compare/build-line-length-comparator.js'
|
||||
import { compareAlphabetically } from '../../utils/compare/compare-alphabetically.js'
|
||||
import { compareByCustomSort } from '../../utils/compare/compare-by-custom-sort.js'
|
||||
import { unsortedComparator } from '../../utils/compare/unsorted-comparator.js'
|
||||
import { compareNaturally } from '../../utils/compare/compare-naturally.js'
|
||||
import { defaultComparatorByOptionsComputer } from '../../utils/compare/default-comparator-by-options-computer.js'
|
||||
/**
|
||||
* Builds a comparator computer function for sorting enum members.
|
||||
*
|
||||
* Creates a function that returns the appropriate comparator based on the
|
||||
* sorting options and whether the enum is numeric. Handles sorting by name or
|
||||
* by value depending on the `sortByValue` option.
|
||||
*
|
||||
* @param isNumericEnum - Whether the enum contains only numeric values.
|
||||
* @returns A comparator computer function that creates comparators from
|
||||
* options.
|
||||
*/
|
||||
function buildComparatorByOptionsComputer(isNumericEnum) {
|
||||
return options => {
|
||||
switch (options.sortByValue) {
|
||||
case 'ifNumericEnum':
|
||||
if (isNumericEnum) {
|
||||
return byNumericValueComparatorComputer(options)
|
||||
}
|
||||
return defaultComparatorByOptionsComputer(options)
|
||||
case 'always':
|
||||
if (isNumericEnum) {
|
||||
return byNumericValueComparatorComputer(options)
|
||||
}
|
||||
return byNonNumericValueComparatorComputer(options)
|
||||
case 'never':
|
||||
return defaultComparatorByOptionsComputer(options)
|
||||
/* v8 ignore next 2 -- @preserve Exhaustive guard. */
|
||||
default:
|
||||
throw new UnreachableCaseError(options.sortByValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
var byNonNumericValueComparatorComputer = options => {
|
||||
switch (options.type) {
|
||||
/* v8 ignore next 2 -- @preserve Untested for now as not a relevant sort for this rule. */
|
||||
case 'subgroup-order':
|
||||
return defaultComparatorByOptionsComputer(options)
|
||||
case 'alphabetical':
|
||||
return (a, b) =>
|
||||
compareAlphabetically(a.value ?? '', b.value ?? '', options)
|
||||
case 'line-length':
|
||||
return buildLineLengthComparator(options)
|
||||
case 'unsorted':
|
||||
return unsortedComparator
|
||||
case 'natural':
|
||||
return (a, b) => compareNaturally(a.value ?? '', b.value ?? '', options)
|
||||
case 'custom':
|
||||
return (a, b) =>
|
||||
compareByCustomSort(a.value ?? '', b.value ?? '', options)
|
||||
/* v8 ignore next 2 -- @preserve Exhaustive guard. */
|
||||
default:
|
||||
throw new UnreachableCaseError(options.type)
|
||||
}
|
||||
}
|
||||
var byNumericValueComparatorComputer = options => {
|
||||
switch (options.type) {
|
||||
/* v8 ignore next 2 -- @preserve Untested for now as not a relevant sort for this rule. */
|
||||
case 'subgroup-order':
|
||||
return defaultComparatorByOptionsComputer(options)
|
||||
case 'alphabetical':
|
||||
case 'line-length':
|
||||
case 'natural':
|
||||
case 'custom':
|
||||
return (a, b) =>
|
||||
compareNaturally(
|
||||
a.numericValue.toString(),
|
||||
b.numericValue.toString(),
|
||||
options,
|
||||
)
|
||||
case 'unsorted':
|
||||
return unsortedComparator
|
||||
/* v8 ignore next 2 -- @preserve Exhaustive guard. */
|
||||
default:
|
||||
throw new UnreachableCaseError(options.type)
|
||||
}
|
||||
}
|
||||
export { buildComparatorByOptionsComputer }
|
||||
Generated
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
import { TSESLint } from '@typescript-eslint/utils'
|
||||
import { SortEnumsSortingNode } from './types.js'
|
||||
type SortingNodeWithoutDependencies = Omit<SortEnumsSortingNode, 'dependencies'>
|
||||
export declare function computeDependenciesBySortingNode({
|
||||
sortingNodes,
|
||||
sourceCode,
|
||||
enumName,
|
||||
}: {
|
||||
sortingNodes: SortingNodeWithoutDependencies[]
|
||||
sourceCode: TSESLint.SourceCode
|
||||
enumName: string
|
||||
}): Map<SortingNodeWithoutDependencies, SortingNodeWithoutDependencies[]>
|
||||
export {}
|
||||
Generated
Vendored
+51
@@ -0,0 +1,51 @@
|
||||
import { computeDependenciesBySortingNode as computeDependenciesBySortingNode$1 } from '../../utils/compute-dependencies-by-sorting-node.js'
|
||||
import { computeParentNodesWithTypes } from '../../utils/compute-parent-nodes-with-types.js'
|
||||
import { doesSortingNodeHaveOneOfDependencyNames } from '../../utils/does-sorting-node-have-one-of-dependency-names.js'
|
||||
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
|
||||
function computeDependenciesBySortingNode({
|
||||
sortingNodes,
|
||||
sourceCode,
|
||||
enumName,
|
||||
}) {
|
||||
return computeDependenciesBySortingNode$1({
|
||||
additionalIdentifierDependenciesComputer:
|
||||
buildAdditionalIdentifierDependenciesComputer({
|
||||
sortingNodes,
|
||||
enumName,
|
||||
}),
|
||||
sortingNodes,
|
||||
sourceCode,
|
||||
})
|
||||
}
|
||||
function buildAdditionalIdentifierDependenciesComputer({
|
||||
sortingNodes,
|
||||
enumName,
|
||||
}) {
|
||||
return ({ referencingSortingNode, reference }) => {
|
||||
if (reference.identifier.name !== enumName) {
|
||||
return []
|
||||
}
|
||||
let relatedIdentifiers = computeMemberExpressionIdentifiers(
|
||||
reference.identifier,
|
||||
referencingSortingNode,
|
||||
)
|
||||
return sortingNodes.filter(sortingNode =>
|
||||
doesSortingNodeHaveOneOfDependencyNames(sortingNode, relatedIdentifiers),
|
||||
)
|
||||
}
|
||||
}
|
||||
function computeMemberExpressionIdentifiers(
|
||||
identifier,
|
||||
referencingSortingNode,
|
||||
) {
|
||||
return computeParentNodesWithTypes({
|
||||
allowedTypes: [AST_NODE_TYPES.MemberExpression],
|
||||
maxParent: referencingSortingNode.node,
|
||||
consecutiveOnly: true,
|
||||
node: identifier,
|
||||
})
|
||||
.map(node => node.property)
|
||||
.filter(property => property.type === AST_NODE_TYPES.Identifier)
|
||||
.map(property => property.name)
|
||||
}
|
||||
export { computeDependenciesBySortingNode }
|
||||
Generated
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
import { TSESTree } from '@typescript-eslint/types'
|
||||
/**
|
||||
* Extract dependencies from an enum.
|
||||
*
|
||||
* @deprecated - To remove when experimental dependency detection is the only
|
||||
* option.
|
||||
* @param expression - The enum or class declaration node.
|
||||
* @param enumName - The name of the enum being processed.
|
||||
* @returns The list of dependencies.
|
||||
*/
|
||||
export declare function computeDependencies(
|
||||
expression: TSESTree.Expression,
|
||||
enumName: string,
|
||||
): string[]
|
||||
Generated
Vendored
+85
@@ -0,0 +1,85 @@
|
||||
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
|
||||
/**
|
||||
* Extract dependencies from an enum.
|
||||
*
|
||||
* @deprecated - To remove when experimental dependency detection is the only
|
||||
* option.
|
||||
* @param expression - The enum or class declaration node.
|
||||
* @param enumName - The name of the enum being processed.
|
||||
* @returns The list of dependencies.
|
||||
*/
|
||||
function computeDependencies(expression, enumName) {
|
||||
let dependencies = []
|
||||
let stack = [expression]
|
||||
while (stack.length > 0) {
|
||||
let node = stack.pop()
|
||||
switch (node.type) {
|
||||
case AST_NODE_TYPES.MemberExpression:
|
||||
if (
|
||||
node.object.type === AST_NODE_TYPES.Identifier &&
|
||||
node.object.name === enumName &&
|
||||
node.property.type === AST_NODE_TYPES.Identifier
|
||||
) {
|
||||
dependencies.push(node.property.name)
|
||||
}
|
||||
break
|
||||
case AST_NODE_TYPES.Identifier:
|
||||
dependencies.push(node.name)
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
if ('alternate' in node && node.alternate) {
|
||||
stack.push(node.alternate)
|
||||
}
|
||||
if ('argument' in node && node.argument) {
|
||||
stack.push(node.argument)
|
||||
}
|
||||
if ('arguments' in node) {
|
||||
stack.push(...node.arguments)
|
||||
}
|
||||
if ('consequent' in node) {
|
||||
if (Array.isArray(node.consequent)) {
|
||||
/* v8 ignore if -- @preserve Unsure if we can reach it. */
|
||||
stack.push(...node.consequent)
|
||||
} else {
|
||||
stack.push(node.consequent)
|
||||
}
|
||||
}
|
||||
if ('elements' in node) {
|
||||
let elements = node.elements.filter(currentNode => currentNode !== null)
|
||||
stack.push(...elements)
|
||||
}
|
||||
if ('expressions' in node) {
|
||||
stack.push(...node.expressions)
|
||||
}
|
||||
if ('key' in node) {
|
||||
stack.push(node.key)
|
||||
}
|
||||
if ('left' in node) {
|
||||
stack.push(node.left)
|
||||
}
|
||||
if ('object' in node) {
|
||||
stack.push(node.object)
|
||||
}
|
||||
if ('properties' in node) {
|
||||
stack.push(...node.properties)
|
||||
}
|
||||
if ('right' in node) {
|
||||
stack.push(node.right)
|
||||
}
|
||||
if ('test' in node && node.test) {
|
||||
stack.push(node.test)
|
||||
}
|
||||
if (
|
||||
'value' in node &&
|
||||
node.value &&
|
||||
typeof node.value === 'object' &&
|
||||
'type' in node.value
|
||||
) {
|
||||
stack.push(node.value)
|
||||
}
|
||||
}
|
||||
return dependencies
|
||||
}
|
||||
export { computeDependencies }
|
||||
Generated
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
import { TSESTree } from '@typescript-eslint/types'
|
||||
/**
|
||||
* Extracts a numeric value from an AST expression node.
|
||||
*
|
||||
* Handles literal numbers, binary expressions, and unary expressions
|
||||
* recursively to compute the final numeric value.
|
||||
*
|
||||
* @param expression - The AST node to evaluate.
|
||||
* @returns The numeric value of the expression, or null if not evaluable.
|
||||
*/
|
||||
export declare function computeExpressionNumberValue(
|
||||
expression: TSESTree.Node,
|
||||
): number | null
|
||||
Generated
Vendored
+105
@@ -0,0 +1,105 @@
|
||||
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
|
||||
/**
|
||||
* Extracts a numeric value from an AST expression node.
|
||||
*
|
||||
* Handles literal numbers, binary expressions, and unary expressions
|
||||
* recursively to compute the final numeric value.
|
||||
*
|
||||
* @param expression - The AST node to evaluate.
|
||||
* @returns The numeric value of the expression, or null if not evaluable.
|
||||
*/
|
||||
function computeExpressionNumberValue(expression) {
|
||||
switch (expression.type) {
|
||||
case AST_NODE_TYPES.BinaryExpression:
|
||||
return computeBinaryExpressionNumberValue(
|
||||
expression.left,
|
||||
expression.right,
|
||||
expression.operator,
|
||||
)
|
||||
case AST_NODE_TYPES.UnaryExpression:
|
||||
return computeUnaryExpressionNumberValue(
|
||||
expression.argument,
|
||||
expression.operator,
|
||||
)
|
||||
case AST_NODE_TYPES.Literal:
|
||||
return typeof expression.value === 'number' ? expression.value : null
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Evaluates a binary expression to compute its numeric value.
|
||||
*
|
||||
* Supports arithmetic operators (+, -, *, /, %, **), bitwise operators (|, &,
|
||||
* ^, <<, >>), and returns null for unsupported expressions.
|
||||
*
|
||||
* @param leftExpression - The left operand of the binary expression.
|
||||
* @param rightExpression - The right operand of the binary expression.
|
||||
* @param operator - The operator string (e.g., '+', '-', '*', '|').
|
||||
* @returns The computed numeric value, or null if it cannot be evaluated.
|
||||
*/
|
||||
function computeBinaryExpressionNumberValue(
|
||||
leftExpression,
|
||||
rightExpression,
|
||||
operator,
|
||||
) {
|
||||
let left = computeExpressionNumberValue(leftExpression)
|
||||
let right = computeExpressionNumberValue(rightExpression)
|
||||
if (left === null || right === null) {
|
||||
return null
|
||||
}
|
||||
switch (operator) {
|
||||
case '**':
|
||||
return left ** right
|
||||
case '>>':
|
||||
return left >> right
|
||||
case '<<':
|
||||
return left << right
|
||||
case '+':
|
||||
return left + right
|
||||
case '-':
|
||||
return left - right
|
||||
case '*':
|
||||
return left * right
|
||||
case '/':
|
||||
return left / right
|
||||
case '%':
|
||||
return left % right
|
||||
case '|':
|
||||
return left | right
|
||||
case '&':
|
||||
return left & right
|
||||
case '^':
|
||||
return left ^ right
|
||||
/* v8 ignore next 2 -- @preserve Unsure if we can reach it. */
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Evaluates a unary expression to compute its numeric value.
|
||||
*
|
||||
* Supports unary plus (+), minus (-), and bitwise NOT (~) operators.
|
||||
*
|
||||
* @param argumentExpression - The operand of the unary expression.
|
||||
* @param operator - The operator string (e.g., '+', '-', '~').
|
||||
* @returns The computed numeric value, or null if it cannot be evaluated.
|
||||
*/
|
||||
function computeUnaryExpressionNumberValue(argumentExpression, operator) {
|
||||
let argument = computeExpressionNumberValue(argumentExpression)
|
||||
if (argument === null) {
|
||||
return null
|
||||
}
|
||||
switch (operator) {
|
||||
case '+':
|
||||
return argument
|
||||
case '-':
|
||||
return -argument
|
||||
case '~':
|
||||
return ~argument
|
||||
/* v8 ignore next 2 -- @preserve Unsure if we can reach it. */
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
export { computeExpressionNumberValue }
|
||||
Generated
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
import { RuleContext } from '@typescript-eslint/utils/ts-eslint'
|
||||
import { TSESTree } from '@typescript-eslint/types'
|
||||
import { Options } from './types.js'
|
||||
/**
|
||||
* Computes the matched context options for a given enum node.
|
||||
*
|
||||
* @param params - Parameters.
|
||||
* @param params.enumMembers - The enum members of the enum declaration node.
|
||||
* @param params.matchedAstSelectors - The matched AST selectors for an enum
|
||||
* node.
|
||||
* @param params.context - The rule context.
|
||||
* @returns The matched context options or undefined if none match.
|
||||
*/
|
||||
export declare function computeMatchedContextOptions<
|
||||
MessageIds extends string,
|
||||
>({
|
||||
matchedAstSelectors,
|
||||
enumMembers,
|
||||
context,
|
||||
}: {
|
||||
context: Readonly<RuleContext<MessageIds, Options>>
|
||||
matchedAstSelectors: ReadonlySet<string>
|
||||
enumMembers: TSESTree.TSEnumMember[]
|
||||
}): Options[number] | undefined
|
||||
Generated
Vendored
+48
@@ -0,0 +1,48 @@
|
||||
import { passesAllNamesMatchPatternFilter } from '../../utils/context-matching/passes-all-names-match-pattern-filter.js'
|
||||
import { passesAstSelectorFilter } from '../../utils/context-matching/passes-ast-selector-filter.js'
|
||||
import { computeNodeName } from './compute-node-name.js'
|
||||
/**
|
||||
* Computes the matched context options for a given enum node.
|
||||
*
|
||||
* @param params - Parameters.
|
||||
* @param params.enumMembers - The enum members of the enum declaration node.
|
||||
* @param params.matchedAstSelectors - The matched AST selectors for an enum
|
||||
* node.
|
||||
* @param params.context - The rule context.
|
||||
* @returns The matched context options or undefined if none match.
|
||||
*/
|
||||
function computeMatchedContextOptions({
|
||||
matchedAstSelectors,
|
||||
enumMembers,
|
||||
context,
|
||||
}) {
|
||||
let nodeNames = enumMembers.map(enumMember =>
|
||||
computeNodeName({
|
||||
sourceCode: context.sourceCode,
|
||||
node: enumMember,
|
||||
}),
|
||||
)
|
||||
return context.options.find(options =>
|
||||
isContextOptionMatching({
|
||||
matchedAstSelectors,
|
||||
nodeNames,
|
||||
options,
|
||||
}),
|
||||
)
|
||||
}
|
||||
function isContextOptionMatching({ matchedAstSelectors, nodeNames, options }) {
|
||||
if (!options.useConfigurationIf) {
|
||||
return true
|
||||
}
|
||||
return (
|
||||
passesAllNamesMatchPatternFilter({
|
||||
allNamesMatchPattern: options.useConfigurationIf.allNamesMatchPattern,
|
||||
nodeNames,
|
||||
}) &&
|
||||
passesAstSelectorFilter({
|
||||
matchesAstSelector: options.useConfigurationIf.matchesAstSelector,
|
||||
matchedAstSelectors,
|
||||
})
|
||||
)
|
||||
}
|
||||
export { computeMatchedContextOptions }
|
||||
Generated
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
import { TSESTree } from '@typescript-eslint/types'
|
||||
import { TSESLint } from '@typescript-eslint/utils'
|
||||
/**
|
||||
* Computes the name of an enum member node.
|
||||
*
|
||||
* @param params - Parameters for group-based sorting.
|
||||
* @param params.node - The enum member node.
|
||||
* @param params.sourceCode - The source code object.
|
||||
* @returns The computed name of the enum member node.
|
||||
*/
|
||||
export declare function computeNodeName({
|
||||
sourceCode,
|
||||
node,
|
||||
}: {
|
||||
sourceCode: TSESLint.SourceCode
|
||||
node: TSESTree.TSEnumMember
|
||||
}): string
|
||||
Generated
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
|
||||
/**
|
||||
* Computes the name of an enum member node.
|
||||
*
|
||||
* @param params - Parameters for group-based sorting.
|
||||
* @param params.node - The enum member node.
|
||||
* @param params.sourceCode - The source code object.
|
||||
* @returns The computed name of the enum member node.
|
||||
*/
|
||||
function computeNodeName({ sourceCode, node }) {
|
||||
return node.id.type === AST_NODE_TYPES.Literal ?
|
||||
node.id.value
|
||||
: sourceCode.getText(node.id)
|
||||
}
|
||||
export { computeNodeName }
|
||||
Generated
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
import { RuleContext } from '@typescript-eslint/utils/ts-eslint'
|
||||
import { TSESTree } from '@typescript-eslint/types'
|
||||
import { MessageId, Options } from './types.js'
|
||||
export declare let defaultOptions: Required<Options[number]>
|
||||
export declare function sortEnum({
|
||||
matchedAstSelectors,
|
||||
context,
|
||||
node,
|
||||
}: {
|
||||
context: Readonly<RuleContext<MessageId, Options>>
|
||||
matchedAstSelectors: ReadonlySet<string>
|
||||
node: TSESTree.TSEnumDeclaration
|
||||
}): void
|
||||
Generated
Vendored
+179
@@ -0,0 +1,179 @@
|
||||
import { populateSortingNodeGroupsWithDependencies } from '../../utils/populate-sorting-node-groups-with-dependencies.js'
|
||||
import { validateNewlinesAndPartitionConfiguration } from '../../utils/validate-newlines-and-partition-configuration.js'
|
||||
import { buildOptionsByGroupIndexComputer } from '../../utils/build-options-by-group-index-computer.js'
|
||||
import { validateCustomSortConfiguration } from '../../utils/validate-custom-sort-configuration.js'
|
||||
import { validateGroupsConfiguration } from '../../utils/validate-groups-configuration.js'
|
||||
import { sortNodesByDependencies } from '../../utils/sort-nodes-by-dependencies.js'
|
||||
import { getEslintDisabledLines } from '../../utils/get-eslint-disabled-lines.js'
|
||||
import { doesCustomGroupMatch } from '../../utils/does-custom-group-match.js'
|
||||
import { isNodeEslintDisabled } from '../../utils/is-node-eslint-disabled.js'
|
||||
import { sortNodesByGroups } from '../../utils/sort-nodes-by-groups.js'
|
||||
import { reportAllErrors } from '../../utils/report-all-errors.js'
|
||||
import { shouldPartition } from '../../utils/should-partition.js'
|
||||
import { computeGroup } from '../../utils/compute-group.js'
|
||||
import { rangeToDiff } from '../../utils/range-to-diff.js'
|
||||
import { getSettings } from '../../utils/get-settings.js'
|
||||
import { isSortable } from '../../utils/is-sortable.js'
|
||||
import { complete } from '../../utils/complete.js'
|
||||
import { getEnumMembers } from '../../utils/get-enum-members.js'
|
||||
import {
|
||||
DEPENDENCY_ORDER_ERROR_ID,
|
||||
EXTRA_SPACING_ERROR_ID,
|
||||
GROUP_ORDER_ERROR_ID,
|
||||
MISSED_SPACING_ERROR_ID,
|
||||
ORDER_ERROR_ID,
|
||||
} from './types.js'
|
||||
import { computeDependenciesBySortingNode } from './compute-dependencies-by-sorting-node.js'
|
||||
import { buildComparatorByOptionsComputer } from './build-comparator-by-options-computer.js'
|
||||
import { computeExpressionNumberValue } from './compute-expression-number-value.js'
|
||||
import { computeNodeName } from './compute-node-name.js'
|
||||
import { computeMatchedContextOptions } from './compute-matched-context-options.js'
|
||||
import { computeDependencies } from './compute-dependencies.js'
|
||||
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
|
||||
var defaultOptions = {
|
||||
useExperimentalDependencyDetection: true,
|
||||
fallbackSort: { type: 'unsorted' },
|
||||
newlinesInside: 'newlinesBetween',
|
||||
sortByValue: 'ifNumericEnum',
|
||||
partitionByComment: false,
|
||||
partitionByNewLine: false,
|
||||
specialCharacters: 'keep',
|
||||
newlinesBetween: 'ignore',
|
||||
useConfigurationIf: {},
|
||||
type: 'alphabetical',
|
||||
ignoreCase: true,
|
||||
locales: 'en-US',
|
||||
customGroups: [],
|
||||
alphabet: '',
|
||||
order: 'asc',
|
||||
groups: [],
|
||||
}
|
||||
function sortEnum({ matchedAstSelectors, context, node }) {
|
||||
let members = getEnumMembers(node)
|
||||
if (
|
||||
!isSortable(members) ||
|
||||
!members.every(({ initializer }) => initializer)
|
||||
) {
|
||||
return
|
||||
}
|
||||
let settings = getSettings(context.settings)
|
||||
let options = complete(
|
||||
computeMatchedContextOptions({
|
||||
enumMembers: members,
|
||||
matchedAstSelectors,
|
||||
context,
|
||||
}),
|
||||
settings,
|
||||
defaultOptions,
|
||||
)
|
||||
validateCustomSortConfiguration(options)
|
||||
validateGroupsConfiguration({
|
||||
selectors: [],
|
||||
modifiers: [],
|
||||
options,
|
||||
})
|
||||
validateNewlinesAndPartitionConfiguration(options)
|
||||
let { sourceCode, id } = context
|
||||
let eslintDisabledLines = getEslintDisabledLines({
|
||||
ruleName: id,
|
||||
sourceCode,
|
||||
})
|
||||
let optionsByGroupIndexComputer = buildOptionsByGroupIndexComputer(options)
|
||||
let sortingNodeGroups = members.reduce(
|
||||
(accumulator, member) => {
|
||||
let name = computeNodeName({
|
||||
node: member,
|
||||
sourceCode,
|
||||
})
|
||||
let group = computeGroup({
|
||||
customGroupMatcher: customGroup =>
|
||||
doesCustomGroupMatch({
|
||||
elementValue: sourceCode.getText(member.initializer),
|
||||
elementName: name,
|
||||
selectors: [],
|
||||
modifiers: [],
|
||||
customGroup,
|
||||
}),
|
||||
predefinedGroups: [],
|
||||
options,
|
||||
})
|
||||
let lastSortingNode = accumulator.at(-1)?.at(-1)
|
||||
let sortingNode = {
|
||||
dependencies:
|
||||
options.useExperimentalDependencyDetection ?
|
||||
[]
|
||||
: computeDependencies(member.initializer, node.id.name),
|
||||
value:
|
||||
member.initializer?.type === AST_NODE_TYPES.Literal ?
|
||||
(member.initializer.value?.toString() ?? null)
|
||||
: null,
|
||||
isEslintDisabled: isNodeEslintDisabled(member, eslintDisabledLines),
|
||||
numericValue: computeExpressionNumberValue(member.initializer),
|
||||
size: rangeToDiff(member, sourceCode),
|
||||
dependencyNames: [name],
|
||||
node: member,
|
||||
group,
|
||||
name,
|
||||
}
|
||||
if (
|
||||
shouldPartition({
|
||||
lastSortingNode,
|
||||
sortingNode,
|
||||
sourceCode,
|
||||
options,
|
||||
})
|
||||
) {
|
||||
accumulator.push([])
|
||||
}
|
||||
accumulator.at(-1).push({
|
||||
...sortingNode,
|
||||
partitionId: accumulator.length,
|
||||
})
|
||||
return accumulator
|
||||
},
|
||||
[[]],
|
||||
)
|
||||
if (options.useExperimentalDependencyDetection) {
|
||||
sortingNodeGroups = populateSortingNodeGroupsWithDependencies({
|
||||
dependenciesBySortingNode: computeDependenciesBySortingNode({
|
||||
sortingNodes: sortingNodeGroups.flat(),
|
||||
enumName: node.id.name,
|
||||
sourceCode,
|
||||
}),
|
||||
sortingNodeGroups,
|
||||
})
|
||||
}
|
||||
let sortingNodes = sortingNodeGroups.flat()
|
||||
let isNumericEnum = sortingNodes.every(
|
||||
sortingNode => sortingNode.numericValue !== null,
|
||||
)
|
||||
reportAllErrors({
|
||||
availableMessageIds: {
|
||||
missedSpacingBetweenMembers: MISSED_SPACING_ERROR_ID,
|
||||
unexpectedDependencyOrder: DEPENDENCY_ORDER_ERROR_ID,
|
||||
extraSpacingBetweenMembers: EXTRA_SPACING_ERROR_ID,
|
||||
unexpectedGroupOrder: GROUP_ORDER_ERROR_ID,
|
||||
unexpectedOrder: ORDER_ERROR_ID,
|
||||
},
|
||||
sortNodesExcludingEslintDisabled,
|
||||
nodes: sortingNodes,
|
||||
options,
|
||||
context,
|
||||
})
|
||||
function sortNodesExcludingEslintDisabled(ignoreEslintDisabledNodes) {
|
||||
return sortNodesByDependencies(
|
||||
sortingNodeGroups.flatMap(sortingNodeGroup =>
|
||||
sortNodesByGroups({
|
||||
comparatorByOptionsComputer:
|
||||
buildComparatorByOptionsComputer(isNumericEnum),
|
||||
optionsByGroupIndexComputer,
|
||||
ignoreEslintDisabledNodes,
|
||||
nodes: sortingNodeGroup,
|
||||
groups: options.groups,
|
||||
}),
|
||||
),
|
||||
{ ignoreEslintDisabledNodes },
|
||||
)
|
||||
}
|
||||
}
|
||||
export { defaultOptions, sortEnum }
|
||||
Generated
Vendored
+82
@@ -0,0 +1,82 @@
|
||||
import { JSONSchema4 } from '@typescript-eslint/utils/json-schema'
|
||||
import { TSESTree } from '@typescript-eslint/types'
|
||||
import { SortingNodeWithDependencies } from '../../utils/sort-nodes-by-dependencies.js'
|
||||
import { RegexOption, TypeOption } from '../../types/common-options.js'
|
||||
import { AllCommonOptions } from '../../types/all-common-options.js'
|
||||
export type MessageId =
|
||||
| typeof DEPENDENCY_ORDER_ERROR_ID
|
||||
| typeof MISSED_SPACING_ERROR_ID
|
||||
| typeof EXTRA_SPACING_ERROR_ID
|
||||
| typeof GROUP_ORDER_ERROR_ID
|
||||
| typeof ORDER_ERROR_ID
|
||||
export declare const ORDER_ERROR_ID = 'unexpectedEnumsOrder'
|
||||
export declare const GROUP_ORDER_ERROR_ID = 'unexpectedEnumsGroupOrder'
|
||||
export declare const EXTRA_SPACING_ERROR_ID = 'extraSpacingBetweenEnumsMembers'
|
||||
export declare const MISSED_SPACING_ERROR_ID =
|
||||
'missedSpacingBetweenEnumsMembers'
|
||||
export declare const DEPENDENCY_ORDER_ERROR_ID =
|
||||
'unexpectedEnumsDependencyOrder'
|
||||
/**
|
||||
* Configuration options for the sort-enums rule.
|
||||
*
|
||||
* This rule enforces consistent ordering of TypeScript enum members to improve
|
||||
* code readability and maintainability.
|
||||
*/
|
||||
export type Options = Partial<
|
||||
{
|
||||
/**
|
||||
* Conditional configuration based on pattern matching.
|
||||
*/
|
||||
useConfigurationIf: {
|
||||
/**
|
||||
* Regular expression pattern to match against all enum element names.
|
||||
* The rule is only applied when all names match this pattern.
|
||||
*/
|
||||
allNamesMatchPattern?: RegexOption
|
||||
/**
|
||||
* AST selector to match against TSEnumDeclaration nodes.
|
||||
*/
|
||||
matchesAstSelector?: string
|
||||
}
|
||||
/**
|
||||
* Whether to sort enum members by their values instead of names. When
|
||||
* "always", compares enum values; when "never", compares enum member
|
||||
* names.
|
||||
*
|
||||
* @default ifNumericEnum
|
||||
*/
|
||||
sortByValue: 'ifNumericEnum' | 'always' | 'never'
|
||||
/**
|
||||
* Enables experimental dependency detection.
|
||||
*/
|
||||
useExperimentalDependencyDetection: boolean
|
||||
} & AllCommonOptions<
|
||||
TypeOption,
|
||||
AdditionalSortOptions,
|
||||
CustomGroupMatchOptions
|
||||
>
|
||||
>[]
|
||||
export interface SortEnumsSortingNode extends SortingNodeWithDependencies<TSESTree.TSEnumMember> {
|
||||
numericValue: number | null
|
||||
value: string | null
|
||||
}
|
||||
/**
|
||||
* Match options for a custom group.
|
||||
*/
|
||||
interface CustomGroupMatchOptions {
|
||||
/**
|
||||
* Regular expression pattern to match enum member values. Members with
|
||||
* values matching this pattern will be included in this custom group.
|
||||
*/
|
||||
elementValuePattern?: RegexOption
|
||||
}
|
||||
type AdditionalSortOptions = object
|
||||
/**
|
||||
* Additional custom group match options JSON schema. Used by ESLint to validate
|
||||
* rule options at configuration time.
|
||||
*/
|
||||
export declare let additionalCustomGroupMatchOptionsJsonSchema: Record<
|
||||
string,
|
||||
JSONSchema4
|
||||
>
|
||||
export {}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import { buildRegexJsonSchema } from '../../utils/json-schemas/common-json-schemas.js'
|
||||
var ORDER_ERROR_ID = 'unexpectedEnumsOrder'
|
||||
var GROUP_ORDER_ERROR_ID = 'unexpectedEnumsGroupOrder'
|
||||
var EXTRA_SPACING_ERROR_ID = 'extraSpacingBetweenEnumsMembers'
|
||||
var MISSED_SPACING_ERROR_ID = 'missedSpacingBetweenEnumsMembers'
|
||||
var DEPENDENCY_ORDER_ERROR_ID = 'unexpectedEnumsDependencyOrder'
|
||||
/**
|
||||
* Additional custom group match options JSON schema. Used by ESLint to validate
|
||||
* rule options at configuration time.
|
||||
*/
|
||||
var additionalCustomGroupMatchOptionsJsonSchema = {
|
||||
elementValuePattern: buildRegexJsonSchema(),
|
||||
}
|
||||
export {
|
||||
DEPENDENCY_ORDER_ERROR_ID,
|
||||
EXTRA_SPACING_ERROR_ID,
|
||||
GROUP_ORDER_ERROR_ID,
|
||||
MISSED_SPACING_ERROR_ID,
|
||||
ORDER_ERROR_ID,
|
||||
additionalCustomGroupMatchOptionsJsonSchema,
|
||||
}
|
||||
Generated
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
declare const ORDER_ERROR_ID = 'unexpectedExportAttributesOrder'
|
||||
declare const GROUP_ORDER_ERROR_ID = 'unexpectedExportAttributesGroupOrder'
|
||||
declare const EXTRA_SPACING_ERROR_ID = 'extraSpacingBetweenExportAttributes'
|
||||
declare const MISSED_SPACING_ERROR_ID = 'missedSpacingBetweenExportAttributes'
|
||||
type MessageId =
|
||||
| typeof MISSED_SPACING_ERROR_ID
|
||||
| typeof EXTRA_SPACING_ERROR_ID
|
||||
| typeof GROUP_ORDER_ERROR_ID
|
||||
| typeof ORDER_ERROR_ID
|
||||
declare const _default: import('@typescript-eslint/utils/ts-eslint').RuleModule<
|
||||
MessageId,
|
||||
import('...js').SortImportAttributesOptions,
|
||||
{
|
||||
recommended?: boolean
|
||||
},
|
||||
import('@typescript-eslint/utils/ts-eslint').RuleListener
|
||||
> & {
|
||||
name: string
|
||||
}
|
||||
export default _default
|
||||
Generated
Vendored
+72
@@ -0,0 +1,72 @@
|
||||
import {
|
||||
EXTRA_SPACING_ERROR,
|
||||
GROUP_ORDER_ERROR,
|
||||
MISSED_SPACING_ERROR,
|
||||
ORDER_ERROR,
|
||||
} from '../utils/report-errors.js'
|
||||
import { buildAstListeners } from '../utils/build-ast-listeners.js'
|
||||
import { createEslintRule } from '../utils/create-eslint-rule.js'
|
||||
import { sortImportOrExportAttributes } from './sort-import-attributes/sort-import-or-export-attributes.js'
|
||||
import { jsonSchema } from './sort-import-attributes.js'
|
||||
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
|
||||
var ORDER_ERROR_ID = 'unexpectedExportAttributesOrder'
|
||||
var GROUP_ORDER_ERROR_ID = 'unexpectedExportAttributesGroupOrder'
|
||||
var EXTRA_SPACING_ERROR_ID = 'extraSpacingBetweenExportAttributes'
|
||||
var MISSED_SPACING_ERROR_ID = 'missedSpacingBetweenExportAttributes'
|
||||
var defaultOptions = {
|
||||
fallbackSort: { type: 'unsorted' },
|
||||
newlinesInside: 'newlinesBetween',
|
||||
specialCharacters: 'keep',
|
||||
partitionByComment: false,
|
||||
partitionByNewLine: false,
|
||||
newlinesBetween: 'ignore',
|
||||
useConfigurationIf: {},
|
||||
type: 'alphabetical',
|
||||
ignoreCase: true,
|
||||
customGroups: [],
|
||||
locales: 'en-US',
|
||||
alphabet: '',
|
||||
order: 'asc',
|
||||
groups: [],
|
||||
}
|
||||
var sort_export_attributes_default = createEslintRule({
|
||||
meta: {
|
||||
messages: {
|
||||
[MISSED_SPACING_ERROR_ID]: MISSED_SPACING_ERROR,
|
||||
[EXTRA_SPACING_ERROR_ID]: EXTRA_SPACING_ERROR,
|
||||
[GROUP_ORDER_ERROR_ID]: GROUP_ORDER_ERROR,
|
||||
[ORDER_ERROR_ID]: ORDER_ERROR,
|
||||
},
|
||||
docs: {
|
||||
url: 'https://perfectionist.dev/rules/sort-export-attributes',
|
||||
description: 'Enforce sorted export attributes.',
|
||||
recommended: true,
|
||||
},
|
||||
schema: jsonSchema,
|
||||
type: 'suggestion',
|
||||
fixable: 'code',
|
||||
},
|
||||
create: context =>
|
||||
buildAstListeners({
|
||||
nodeTypes: [AST_NODE_TYPES.ExportNamedDeclaration],
|
||||
sorter: sortExportAttributes,
|
||||
context,
|
||||
}),
|
||||
defaultOptions: [defaultOptions],
|
||||
name: 'sort-export-attributes',
|
||||
})
|
||||
function sortExportAttributes({ matchedAstSelectors, context, node }) {
|
||||
sortImportOrExportAttributes({
|
||||
availableMessageIds: {
|
||||
missedSpacingBetweenMembers: MISSED_SPACING_ERROR_ID,
|
||||
extraSpacingBetweenMembers: EXTRA_SPACING_ERROR_ID,
|
||||
unexpectedGroupOrder: GROUP_ORDER_ERROR_ID,
|
||||
unexpectedOrder: ORDER_ERROR_ID,
|
||||
},
|
||||
matchedAstSelectors,
|
||||
defaultOptions,
|
||||
context,
|
||||
node,
|
||||
})
|
||||
}
|
||||
export { sort_export_attributes_default as default }
|
||||
Generated
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
import { Options as SortImportAttributesOptions } from '../sort-import-attributes/types.js'
|
||||
export type Options = SortImportAttributesOptions
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import { TSESLint } from '@typescript-eslint/utils'
|
||||
import { Options } from './sort-exports/types.js'
|
||||
declare const ORDER_ERROR_ID = 'unexpectedExportsOrder'
|
||||
declare const GROUP_ORDER_ERROR_ID = 'unexpectedExportsGroupOrder'
|
||||
declare const EXTRA_SPACING_ERROR_ID = 'extraSpacingBetweenExports'
|
||||
declare const MISSED_SPACING_ERROR_ID = 'missedSpacingBetweenExports'
|
||||
declare const MISSED_COMMENT_ABOVE_ERROR_ID = 'missedCommentAboveExport'
|
||||
type MessageId =
|
||||
| typeof MISSED_COMMENT_ABOVE_ERROR_ID
|
||||
| typeof MISSED_SPACING_ERROR_ID
|
||||
| typeof EXTRA_SPACING_ERROR_ID
|
||||
| typeof GROUP_ORDER_ERROR_ID
|
||||
| typeof ORDER_ERROR_ID
|
||||
declare const _default: TSESLint.RuleModule<
|
||||
MessageId,
|
||||
Options,
|
||||
{
|
||||
recommended?: boolean
|
||||
},
|
||||
TSESLint.RuleListener
|
||||
> & {
|
||||
name: string
|
||||
}
|
||||
export default _default
|
||||
+236
@@ -0,0 +1,236 @@
|
||||
import { buildCommonJsonSchemas } from '../utils/json-schemas/common-json-schemas.js'
|
||||
import { buildCommonGroupsJsonSchemas } from '../utils/json-schemas/common-groups-json-schemas.js'
|
||||
import { UnreachableCaseError } from '../utils/unreachable-case-error.js'
|
||||
import {
|
||||
EXTRA_SPACING_ERROR,
|
||||
GROUP_ORDER_ERROR,
|
||||
MISSED_COMMENT_ABOVE_ERROR,
|
||||
MISSED_SPACING_ERROR,
|
||||
ORDER_ERROR,
|
||||
} from '../utils/report-errors.js'
|
||||
import {
|
||||
partitionByCommentJsonSchema,
|
||||
partitionByNewLineJsonSchema,
|
||||
} from '../utils/json-schemas/common-partition-json-schemas.js'
|
||||
import { validateNewlinesAndPartitionConfiguration } from '../utils/validate-newlines-and-partition-configuration.js'
|
||||
import { defaultComparatorByOptionsComputer } from '../utils/compare/default-comparator-by-options-computer.js'
|
||||
import { buildOptionsByGroupIndexComputer } from '../utils/build-options-by-group-index-computer.js'
|
||||
import { validateCustomSortConfiguration } from '../utils/validate-custom-sort-configuration.js'
|
||||
import { validateGroupsConfiguration } from '../utils/validate-groups-configuration.js'
|
||||
import { generatePredefinedGroups } from '../utils/generate-predefined-groups.js'
|
||||
import { getEslintDisabledLines } from '../utils/get-eslint-disabled-lines.js'
|
||||
import { doesCustomGroupMatch } from '../utils/does-custom-group-match.js'
|
||||
import { isNodeEslintDisabled } from '../utils/is-node-eslint-disabled.js'
|
||||
import { sortNodesByGroups } from '../utils/sort-nodes-by-groups.js'
|
||||
import { reportAllErrors } from '../utils/report-all-errors.js'
|
||||
import { shouldPartition } from '../utils/should-partition.js'
|
||||
import { computeGroup } from '../utils/compute-group.js'
|
||||
import { rangeToDiff } from '../utils/range-to-diff.js'
|
||||
import { getSettings } from '../utils/get-settings.js'
|
||||
import { complete } from '../utils/complete.js'
|
||||
import { createEslintRule } from '../utils/create-eslint-rule.js'
|
||||
import { isNodeOnSingleLine } from '../utils/is-node-on-single-line.js'
|
||||
import {
|
||||
additionalCustomGroupMatchOptionsJsonSchema,
|
||||
allModifiers,
|
||||
allSelectors,
|
||||
} from './sort-exports/types.js'
|
||||
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
|
||||
/**
|
||||
* Cache computed groups by modifiers and selectors for performance.
|
||||
*/
|
||||
var cachedGroupsByModifiersAndSelectors = /* @__PURE__ */ new Map()
|
||||
var ORDER_ERROR_ID = 'unexpectedExportsOrder'
|
||||
var GROUP_ORDER_ERROR_ID = 'unexpectedExportsGroupOrder'
|
||||
var EXTRA_SPACING_ERROR_ID = 'extraSpacingBetweenExports'
|
||||
var MISSED_SPACING_ERROR_ID = 'missedSpacingBetweenExports'
|
||||
var MISSED_COMMENT_ABOVE_ERROR_ID = 'missedCommentAboveExport'
|
||||
var defaultOptions = {
|
||||
fallbackSort: { type: 'unsorted' },
|
||||
newlinesInside: 'newlinesBetween',
|
||||
specialCharacters: 'keep',
|
||||
partitionByComment: false,
|
||||
newlinesBetween: 'ignore',
|
||||
partitionByNewLine: false,
|
||||
type: 'alphabetical',
|
||||
customGroups: [],
|
||||
ignoreCase: true,
|
||||
locales: 'en-US',
|
||||
alphabet: '',
|
||||
order: 'asc',
|
||||
groups: [],
|
||||
}
|
||||
var sort_exports_default = createEslintRule({
|
||||
create: context => {
|
||||
let settings = getSettings(context.settings)
|
||||
let options = complete(context.options.at(0), settings, defaultOptions)
|
||||
validateCustomSortConfiguration(options)
|
||||
validateGroupsConfiguration({
|
||||
modifiers: allModifiers,
|
||||
selectors: allSelectors,
|
||||
options,
|
||||
})
|
||||
validateNewlinesAndPartitionConfiguration(options)
|
||||
let { sourceCode, id } = context
|
||||
let eslintDisabledLines = getEslintDisabledLines({
|
||||
ruleName: id,
|
||||
sourceCode,
|
||||
})
|
||||
let formattedMembers = [[]]
|
||||
function registerNode(node) {
|
||||
if (!node.source) {
|
||||
return
|
||||
}
|
||||
let selector = 'export'
|
||||
let modifiers = [
|
||||
computeExportKindModifier(node),
|
||||
computeExportTypeModifier(node),
|
||||
computeLineCountModifier(node),
|
||||
]
|
||||
let name = node.source.value
|
||||
let group = computeGroup({
|
||||
customGroupMatcher: customGroup =>
|
||||
doesCustomGroupMatch({
|
||||
selectors: [selector],
|
||||
elementName: name,
|
||||
customGroup,
|
||||
modifiers,
|
||||
}),
|
||||
predefinedGroups: generatePredefinedGroups({
|
||||
cache: cachedGroupsByModifiersAndSelectors,
|
||||
selectors: [selector],
|
||||
modifiers,
|
||||
}),
|
||||
options,
|
||||
})
|
||||
let sortingNode = {
|
||||
isEslintDisabled: isNodeEslintDisabled(node, eslintDisabledLines),
|
||||
size: rangeToDiff(node, sourceCode),
|
||||
addSafetySemicolonWhenInline: true,
|
||||
group,
|
||||
name,
|
||||
node,
|
||||
}
|
||||
let lastSortingNode = formattedMembers.at(-1)?.at(-1)
|
||||
if (
|
||||
shouldPartition({
|
||||
lastSortingNode,
|
||||
sortingNode,
|
||||
sourceCode,
|
||||
options,
|
||||
})
|
||||
) {
|
||||
formattedMembers.push([])
|
||||
}
|
||||
formattedMembers.at(-1).push({
|
||||
...sortingNode,
|
||||
partitionId: formattedMembers.length,
|
||||
})
|
||||
}
|
||||
return {
|
||||
'Program:exit': () => {
|
||||
sortExportNodes({
|
||||
formattedMembers,
|
||||
context,
|
||||
options,
|
||||
})
|
||||
},
|
||||
ExportNamedDeclaration: registerNode,
|
||||
ExportAllDeclaration: registerNode,
|
||||
}
|
||||
},
|
||||
meta: {
|
||||
schema: {
|
||||
items: {
|
||||
properties: {
|
||||
...buildCommonJsonSchemas(),
|
||||
...buildCommonGroupsJsonSchemas({
|
||||
additionalCustomGroupMatchProperties:
|
||||
additionalCustomGroupMatchOptionsJsonSchema,
|
||||
}),
|
||||
partitionByComment: partitionByCommentJsonSchema,
|
||||
partitionByNewLine: partitionByNewLineJsonSchema,
|
||||
},
|
||||
additionalProperties: false,
|
||||
type: 'object',
|
||||
},
|
||||
uniqueItems: true,
|
||||
type: 'array',
|
||||
},
|
||||
messages: {
|
||||
[MISSED_COMMENT_ABOVE_ERROR_ID]: MISSED_COMMENT_ABOVE_ERROR,
|
||||
[MISSED_SPACING_ERROR_ID]: MISSED_SPACING_ERROR,
|
||||
[EXTRA_SPACING_ERROR_ID]: EXTRA_SPACING_ERROR,
|
||||
[GROUP_ORDER_ERROR_ID]: GROUP_ORDER_ERROR,
|
||||
[ORDER_ERROR_ID]: ORDER_ERROR,
|
||||
},
|
||||
docs: {
|
||||
url: 'https://perfectionist.dev/rules/sort-exports',
|
||||
description: 'Enforce sorted exports.',
|
||||
recommended: true,
|
||||
},
|
||||
type: 'suggestion',
|
||||
fixable: 'code',
|
||||
},
|
||||
defaultOptions: [defaultOptions],
|
||||
name: 'sort-exports',
|
||||
})
|
||||
function sortExportNodes({ formattedMembers, context, options }) {
|
||||
let optionsByGroupIndexComputer = buildOptionsByGroupIndexComputer(options)
|
||||
let nodes = formattedMembers.flat()
|
||||
reportAllErrors({
|
||||
availableMessageIds: {
|
||||
missedSpacingBetweenMembers: MISSED_SPACING_ERROR_ID,
|
||||
extraSpacingBetweenMembers: EXTRA_SPACING_ERROR_ID,
|
||||
missedCommentAbove: MISSED_COMMENT_ABOVE_ERROR_ID,
|
||||
unexpectedGroupOrder: GROUP_ORDER_ERROR_ID,
|
||||
unexpectedOrder: ORDER_ERROR_ID,
|
||||
},
|
||||
sortNodesExcludingEslintDisabled,
|
||||
options,
|
||||
context,
|
||||
nodes,
|
||||
})
|
||||
function sortNodesExcludingEslintDisabled(ignoreEslintDisabledNodes) {
|
||||
return formattedMembers.flatMap(groupedNodes =>
|
||||
sortNodesByGroups({
|
||||
comparatorByOptionsComputer: defaultComparatorByOptionsComputer,
|
||||
optionsByGroupIndexComputer,
|
||||
ignoreEslintDisabledNodes,
|
||||
groups: options.groups,
|
||||
nodes: groupedNodes,
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
function computeExportKindModifier(node) {
|
||||
let exportKind = 'exportKind' in node ? node.exportKind : void 0
|
||||
switch (exportKind) {
|
||||
case void 0:
|
||||
case 'value':
|
||||
return 'value'
|
||||
case 'type':
|
||||
return 'type'
|
||||
/* v8 ignore next 2 -- @preserve Exhaustive guard. */
|
||||
default:
|
||||
throw new UnreachableCaseError(exportKind)
|
||||
}
|
||||
}
|
||||
function computeExportTypeModifier(node) {
|
||||
switch (node.type) {
|
||||
case AST_NODE_TYPES.ExportNamedDeclaration:
|
||||
return 'named'
|
||||
case AST_NODE_TYPES.ExportAllDeclaration:
|
||||
return 'wildcard'
|
||||
/* v8 ignore next 2 -- @preserve Exhaustive guard. */
|
||||
default:
|
||||
throw new UnreachableCaseError(node)
|
||||
}
|
||||
}
|
||||
function computeLineCountModifier(node) {
|
||||
if (isNodeOnSingleLine(node)) {
|
||||
return 'singleline'
|
||||
}
|
||||
return 'multiline'
|
||||
}
|
||||
export { sort_exports_default as default }
|
||||
Generated
Vendored
+76
@@ -0,0 +1,76 @@
|
||||
import { JSONSchema4 } from '@typescript-eslint/utils/json-schema'
|
||||
import { AllCommonOptions } from '../../types/all-common-options.js'
|
||||
import { TypeOption } from '../../types/common-options.js'
|
||||
/**
|
||||
* Configuration options for the sort-exports rule.
|
||||
*
|
||||
* This rule enforces consistent ordering of re-export statements (`export ...
|
||||
* From '...'`) to improve code organization and maintainability.
|
||||
*/
|
||||
export type Options = Partial<
|
||||
AllCommonOptions<TypeOption, AdditionalSortOptions, CustomGroupMatchOptions>
|
||||
>[]
|
||||
/**
|
||||
* Union type of available export modifiers. Distinguishes between value exports
|
||||
* and type-only exports.
|
||||
*/
|
||||
export type Modifier = (typeof allModifiers)[number]
|
||||
/**
|
||||
* Type of selector for export statements. Currently represents re-export
|
||||
* statements.
|
||||
*/
|
||||
export type Selector = (typeof allSelectors)[number]
|
||||
/**
|
||||
* Additional configuration for a single custom group.
|
||||
*
|
||||
* Custom groups allow fine-grained control over how export statements are
|
||||
* grouped and sorted based on their module names, selectors, and modifiers.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```ts
|
||||
* {
|
||||
* "modifiers": ["type"],
|
||||
* "selector": "export"
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
interface CustomGroupMatchOptions {
|
||||
/**
|
||||
* List of modifiers that exports must have to be included in this group.
|
||||
* Can include 'value' for value exports or 'type' for type exports.
|
||||
*/
|
||||
modifiers?: Modifier[]
|
||||
/**
|
||||
* The selector type for this group. Currently only 'export' is supported
|
||||
* for re-export statements.
|
||||
*/
|
||||
selector?: Selector
|
||||
}
|
||||
type AdditionalSortOptions = object
|
||||
/**
|
||||
* Complete list of available export selectors. Used for validation and JSON
|
||||
* schema generation.
|
||||
*/
|
||||
export declare let allSelectors: readonly ['export']
|
||||
/**
|
||||
* Complete list of available export modifiers. Used for validation and JSON
|
||||
* schema generation.
|
||||
*/
|
||||
export declare let allModifiers: readonly [
|
||||
'value',
|
||||
'type',
|
||||
'named',
|
||||
'wildcard',
|
||||
'multiline',
|
||||
'singleline',
|
||||
]
|
||||
/**
|
||||
* Additional custom group match options JSON schema. Used by ESLint to validate
|
||||
* rule options at configuration time.
|
||||
*/
|
||||
export declare let additionalCustomGroupMatchOptionsJsonSchema: Record<
|
||||
string,
|
||||
JSONSchema4
|
||||
>
|
||||
export {}
|
||||
Generated
Vendored
+34
@@ -0,0 +1,34 @@
|
||||
import {
|
||||
buildCustomGroupModifiersJsonSchema,
|
||||
buildCustomGroupSelectorJsonSchema,
|
||||
} from '../../utils/json-schemas/common-groups-json-schemas.js'
|
||||
/**
|
||||
* Complete list of available export selectors. Used for validation and JSON
|
||||
* schema generation.
|
||||
*/
|
||||
var allSelectors = ['export']
|
||||
/**
|
||||
* Complete list of available export modifiers. Used for validation and JSON
|
||||
* schema generation.
|
||||
*/
|
||||
var allModifiers = [
|
||||
'value',
|
||||
'type',
|
||||
'named',
|
||||
'wildcard',
|
||||
'multiline',
|
||||
'singleline',
|
||||
]
|
||||
/**
|
||||
* Additional custom group match options JSON schema. Used by ESLint to validate
|
||||
* rule options at configuration time.
|
||||
*/
|
||||
var additionalCustomGroupMatchOptionsJsonSchema = {
|
||||
modifiers: buildCustomGroupModifiersJsonSchema(allModifiers),
|
||||
selector: buildCustomGroupSelectorJsonSchema(allSelectors),
|
||||
}
|
||||
export {
|
||||
additionalCustomGroupMatchOptionsJsonSchema,
|
||||
allModifiers,
|
||||
allSelectors,
|
||||
}
|
||||
Generated
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
import { MessageId, Options } from './sort-heritage-clauses/types.js'
|
||||
declare const _default: import('@typescript-eslint/utils/ts-eslint').RuleModule<
|
||||
MessageId,
|
||||
Options,
|
||||
{
|
||||
recommended?: boolean
|
||||
},
|
||||
import('@typescript-eslint/utils/ts-eslint').RuleListener
|
||||
> & {
|
||||
name: string
|
||||
}
|
||||
export default _default
|
||||
Generated
Vendored
+77
@@ -0,0 +1,77 @@
|
||||
import {
|
||||
buildCommonJsonSchemas,
|
||||
buildUseConfigurationIfJsonSchema,
|
||||
matchesAstSelectorJsonSchema,
|
||||
} from '../utils/json-schemas/common-json-schemas.js'
|
||||
import { buildCommonGroupsJsonSchemas } from '../utils/json-schemas/common-groups-json-schemas.js'
|
||||
import {
|
||||
EXTRA_SPACING_ERROR,
|
||||
GROUP_ORDER_ERROR,
|
||||
MISSED_SPACING_ERROR,
|
||||
ORDER_ERROR,
|
||||
} from '../utils/report-errors.js'
|
||||
import {
|
||||
partitionByCommentJsonSchema,
|
||||
partitionByNewLineJsonSchema,
|
||||
} from '../utils/json-schemas/common-partition-json-schemas.js'
|
||||
import { buildAstListeners } from '../utils/build-ast-listeners.js'
|
||||
import { createEslintRule } from '../utils/create-eslint-rule.js'
|
||||
import {
|
||||
EXTRA_SPACING_ERROR_ID,
|
||||
GROUP_ORDER_ERROR_ID,
|
||||
MISSED_SPACING_ERROR_ID,
|
||||
ORDER_ERROR_ID,
|
||||
} from './sort-heritage-clauses/types.js'
|
||||
import {
|
||||
defaultOptions,
|
||||
sortHeritageClause,
|
||||
} from './sort-heritage-clauses/sort-heritage-clause.js'
|
||||
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
|
||||
var sort_heritage_clauses_default = createEslintRule({
|
||||
meta: {
|
||||
schema: {
|
||||
items: {
|
||||
properties: {
|
||||
...buildCommonJsonSchemas(),
|
||||
...buildCommonGroupsJsonSchemas(),
|
||||
useConfigurationIf: buildUseConfigurationIfJsonSchema({
|
||||
additionalProperties: {
|
||||
matchesAstSelector: matchesAstSelectorJsonSchema,
|
||||
},
|
||||
}),
|
||||
partitionByNewLine: partitionByNewLineJsonSchema,
|
||||
partitionByComment: partitionByCommentJsonSchema,
|
||||
},
|
||||
additionalProperties: false,
|
||||
type: 'object',
|
||||
},
|
||||
uniqueItems: true,
|
||||
type: 'array',
|
||||
},
|
||||
messages: {
|
||||
[MISSED_SPACING_ERROR_ID]: MISSED_SPACING_ERROR,
|
||||
[EXTRA_SPACING_ERROR_ID]: EXTRA_SPACING_ERROR,
|
||||
[GROUP_ORDER_ERROR_ID]: GROUP_ORDER_ERROR,
|
||||
[ORDER_ERROR_ID]: ORDER_ERROR,
|
||||
},
|
||||
docs: {
|
||||
url: 'https://perfectionist.dev/rules/sort-heritage-clauses',
|
||||
description: 'Enforce sorted heritage clauses.',
|
||||
recommended: true,
|
||||
},
|
||||
type: 'suggestion',
|
||||
fixable: 'code',
|
||||
},
|
||||
create: context =>
|
||||
buildAstListeners({
|
||||
nodeTypes: [
|
||||
AST_NODE_TYPES.TSInterfaceDeclaration,
|
||||
AST_NODE_TYPES.ClassDeclaration,
|
||||
],
|
||||
sorter: sortHeritageClause,
|
||||
context,
|
||||
}),
|
||||
defaultOptions: [defaultOptions],
|
||||
name: 'sort-heritage-clauses',
|
||||
})
|
||||
export { sort_heritage_clauses_default as default }
|
||||
Generated
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
import { RuleContext } from '@typescript-eslint/utils/ts-eslint'
|
||||
import { TSESTree } from '@typescript-eslint/types'
|
||||
import { Options } from './types.js'
|
||||
/**
|
||||
* Computes the matched context options for a given heritage clause parent node.
|
||||
*
|
||||
* @param params - Parameters.
|
||||
* @param params.heritageClauses - The heritage clauses of the parent node.
|
||||
* @param params.matchedAstSelectors - The matched AST selectors for an object
|
||||
* node.
|
||||
* @param params.context - The rule context.
|
||||
* @returns The matched context options or undefined if none match.
|
||||
*/
|
||||
export declare function computeMatchedContextOptions<
|
||||
MessageIds extends string,
|
||||
>({
|
||||
matchedAstSelectors,
|
||||
heritageClauses,
|
||||
context,
|
||||
}: {
|
||||
heritageClauses: TSESTree.TSInterfaceHeritage[] | TSESTree.TSClassImplements[]
|
||||
context: Readonly<RuleContext<MessageIds, Options>>
|
||||
matchedAstSelectors: ReadonlySet<string>
|
||||
}): Options[number] | undefined
|
||||
Generated
Vendored
+45
@@ -0,0 +1,45 @@
|
||||
import { passesAllNamesMatchPatternFilter } from '../../utils/context-matching/passes-all-names-match-pattern-filter.js'
|
||||
import { passesAstSelectorFilter } from '../../utils/context-matching/passes-ast-selector-filter.js'
|
||||
import { computeNodeName } from './compute-node-name.js'
|
||||
/**
|
||||
* Computes the matched context options for a given heritage clause parent node.
|
||||
*
|
||||
* @param params - Parameters.
|
||||
* @param params.heritageClauses - The heritage clauses of the parent node.
|
||||
* @param params.matchedAstSelectors - The matched AST selectors for an object
|
||||
* node.
|
||||
* @param params.context - The rule context.
|
||||
* @returns The matched context options or undefined if none match.
|
||||
*/
|
||||
function computeMatchedContextOptions({
|
||||
matchedAstSelectors,
|
||||
heritageClauses,
|
||||
context,
|
||||
}) {
|
||||
let nodeNames = heritageClauses.map(clause =>
|
||||
computeNodeName(clause.expression),
|
||||
)
|
||||
return context.options.find(options =>
|
||||
isContextOptionMatching({
|
||||
matchedAstSelectors,
|
||||
nodeNames,
|
||||
options,
|
||||
}),
|
||||
)
|
||||
}
|
||||
function isContextOptionMatching({ matchedAstSelectors, nodeNames, options }) {
|
||||
if (!options.useConfigurationIf) {
|
||||
return true
|
||||
}
|
||||
return (
|
||||
passesAllNamesMatchPatternFilter({
|
||||
allNamesMatchPattern: options.useConfigurationIf.allNamesMatchPattern,
|
||||
nodeNames,
|
||||
}) &&
|
||||
passesAstSelectorFilter({
|
||||
matchesAstSelector: options.useConfigurationIf.matchesAstSelector,
|
||||
matchedAstSelectors,
|
||||
})
|
||||
)
|
||||
}
|
||||
export { computeMatchedContextOptions }
|
||||
Generated
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
import { TSESTree } from '@typescript-eslint/types'
|
||||
/**
|
||||
* Recursively extracts the name from a heritage clause expression.
|
||||
*
|
||||
* For simple identifiers, returns the name directly. For member expressions
|
||||
* (like `Namespace.Class`), recursively extracts the property name.
|
||||
*
|
||||
* @param expression - The heritage clause expression AST node.
|
||||
* @returns The extracted name string from the expression.
|
||||
*/
|
||||
export declare function computeNodeName(
|
||||
expression: TSESTree.PrivateIdentifier | TSESTree.Expression,
|
||||
): string
|
||||
Generated
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
|
||||
/**
|
||||
* Recursively extracts the name from a heritage clause expression.
|
||||
*
|
||||
* For simple identifiers, returns the name directly. For member expressions
|
||||
* (like `Namespace.Class`), recursively extracts the property name.
|
||||
*
|
||||
* @param expression - The heritage clause expression AST node.
|
||||
* @returns The extracted name string from the expression.
|
||||
*/
|
||||
function computeNodeName(expression) {
|
||||
if (expression.type === AST_NODE_TYPES.Identifier) {
|
||||
return expression.name
|
||||
}
|
||||
/* v8 ignore else -- @preserve Exhaustive guard for unsupported expressions. */
|
||||
if ('property' in expression) {
|
||||
return computeNodeName(expression.property)
|
||||
}
|
||||
/* v8 ignore next -- @preserve Should never throw. */
|
||||
throw new Error(
|
||||
'Unexpected heritage clause expression. Please report this issue here: https://github.com/azat-io/eslint-plugin-perfectionist/issues',
|
||||
)
|
||||
}
|
||||
export { computeNodeName }
|
||||
Generated
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
import { TSESLint } from '@typescript-eslint/utils'
|
||||
import { TSESTree } from '@typescript-eslint/types'
|
||||
import { MessageId, Options } from './types.js'
|
||||
export declare let defaultOptions: Required<Options[number]>
|
||||
export declare function sortHeritageClause({
|
||||
matchedAstSelectors,
|
||||
context,
|
||||
node,
|
||||
}: {
|
||||
node: TSESTree.TSInterfaceDeclaration | TSESTree.ClassDeclaration
|
||||
context: TSESLint.RuleContext<MessageId, Options>
|
||||
matchedAstSelectors: ReadonlySet<string>
|
||||
}): void
|
||||
Generated
Vendored
+138
@@ -0,0 +1,138 @@
|
||||
import { validateNewlinesAndPartitionConfiguration } from '../../utils/validate-newlines-and-partition-configuration.js'
|
||||
import { defaultComparatorByOptionsComputer } from '../../utils/compare/default-comparator-by-options-computer.js'
|
||||
import { buildOptionsByGroupIndexComputer } from '../../utils/build-options-by-group-index-computer.js'
|
||||
import { validateCustomSortConfiguration } from '../../utils/validate-custom-sort-configuration.js'
|
||||
import { validateGroupsConfiguration } from '../../utils/validate-groups-configuration.js'
|
||||
import { getEslintDisabledLines } from '../../utils/get-eslint-disabled-lines.js'
|
||||
import { doesCustomGroupMatch } from '../../utils/does-custom-group-match.js'
|
||||
import { isNodeEslintDisabled } from '../../utils/is-node-eslint-disabled.js'
|
||||
import { sortNodesByGroups } from '../../utils/sort-nodes-by-groups.js'
|
||||
import { reportAllErrors } from '../../utils/report-all-errors.js'
|
||||
import { shouldPartition } from '../../utils/should-partition.js'
|
||||
import { computeGroup } from '../../utils/compute-group.js'
|
||||
import { rangeToDiff } from '../../utils/range-to-diff.js'
|
||||
import { getSettings } from '../../utils/get-settings.js'
|
||||
import { isSortable } from '../../utils/is-sortable.js'
|
||||
import { complete } from '../../utils/complete.js'
|
||||
import {
|
||||
EXTRA_SPACING_ERROR_ID,
|
||||
GROUP_ORDER_ERROR_ID,
|
||||
MISSED_SPACING_ERROR_ID,
|
||||
ORDER_ERROR_ID,
|
||||
} from './types.js'
|
||||
import { computeNodeName } from './compute-node-name.js'
|
||||
import { computeMatchedContextOptions } from './compute-matched-context-options.js'
|
||||
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
|
||||
var defaultOptions = {
|
||||
fallbackSort: { type: 'unsorted' },
|
||||
newlinesInside: 'newlinesBetween',
|
||||
specialCharacters: 'keep',
|
||||
newlinesBetween: 'ignore',
|
||||
partitionByNewLine: false,
|
||||
partitionByComment: false,
|
||||
useConfigurationIf: {},
|
||||
type: 'alphabetical',
|
||||
ignoreCase: true,
|
||||
customGroups: [],
|
||||
locales: 'en-US',
|
||||
alphabet: '',
|
||||
order: 'asc',
|
||||
groups: [],
|
||||
}
|
||||
function sortHeritageClause({ matchedAstSelectors, context, node }) {
|
||||
let heritageClauses =
|
||||
node.type === AST_NODE_TYPES.TSInterfaceDeclaration ?
|
||||
node.extends
|
||||
: node.implements
|
||||
if (!isSortable(heritageClauses)) {
|
||||
return
|
||||
}
|
||||
let settings = getSettings(context.settings)
|
||||
let options = complete(
|
||||
computeMatchedContextOptions({
|
||||
matchedAstSelectors,
|
||||
heritageClauses,
|
||||
context,
|
||||
}),
|
||||
settings,
|
||||
defaultOptions,
|
||||
)
|
||||
validateCustomSortConfiguration(options)
|
||||
validateGroupsConfiguration({
|
||||
modifiers: [],
|
||||
selectors: [],
|
||||
options,
|
||||
})
|
||||
validateNewlinesAndPartitionConfiguration(options)
|
||||
let { sourceCode, id } = context
|
||||
let eslintDisabledLines = getEslintDisabledLines({
|
||||
ruleName: id,
|
||||
sourceCode,
|
||||
})
|
||||
let optionsByGroupIndexComputer = buildOptionsByGroupIndexComputer(options)
|
||||
let formattedMembers = [[]]
|
||||
for (let heritageClause of heritageClauses) {
|
||||
let name = computeNodeName(heritageClause.expression)
|
||||
let group = computeGroup({
|
||||
customGroupMatcher: customGroup =>
|
||||
doesCustomGroupMatch({
|
||||
elementName: name,
|
||||
selectors: [],
|
||||
modifiers: [],
|
||||
customGroup,
|
||||
}),
|
||||
predefinedGroups: [],
|
||||
options,
|
||||
})
|
||||
let sortingNode = {
|
||||
isEslintDisabled: isNodeEslintDisabled(
|
||||
heritageClause,
|
||||
eslintDisabledLines,
|
||||
),
|
||||
size: rangeToDiff(heritageClause, sourceCode),
|
||||
node: heritageClause,
|
||||
partitionId: 0,
|
||||
group,
|
||||
name,
|
||||
}
|
||||
let lastSortingNode = formattedMembers.at(-1)?.at(-1)
|
||||
if (
|
||||
shouldPartition({
|
||||
lastSortingNode,
|
||||
sortingNode,
|
||||
sourceCode,
|
||||
options,
|
||||
})
|
||||
) {
|
||||
formattedMembers.push([])
|
||||
}
|
||||
formattedMembers.at(-1).push(sortingNode)
|
||||
}
|
||||
for (let nodes of formattedMembers) {
|
||||
function createSortNodesExcludingEslintDisabled(sortingNodes) {
|
||||
return function (ignoreEslintDisabledNodes) {
|
||||
return sortNodesByGroups({
|
||||
comparatorByOptionsComputer: defaultComparatorByOptionsComputer,
|
||||
optionsByGroupIndexComputer,
|
||||
ignoreEslintDisabledNodes,
|
||||
groups: options.groups,
|
||||
nodes: sortingNodes,
|
||||
})
|
||||
}
|
||||
}
|
||||
reportAllErrors({
|
||||
availableMessageIds: {
|
||||
missedSpacingBetweenMembers: MISSED_SPACING_ERROR_ID,
|
||||
extraSpacingBetweenMembers: EXTRA_SPACING_ERROR_ID,
|
||||
unexpectedGroupOrder: GROUP_ORDER_ERROR_ID,
|
||||
unexpectedOrder: ORDER_ERROR_ID,
|
||||
},
|
||||
sortNodesExcludingEslintDisabled:
|
||||
createSortNodesExcludingEslintDisabled(nodes),
|
||||
options,
|
||||
context,
|
||||
nodes,
|
||||
})
|
||||
}
|
||||
}
|
||||
export { defaultOptions, sortHeritageClause }
|
||||
Generated
Vendored
+43
@@ -0,0 +1,43 @@
|
||||
import { RegexOption, TypeOption } from '../../types/common-options.js'
|
||||
import { AllCommonOptions } from '../../types/all-common-options.js'
|
||||
export type MessageId =
|
||||
| typeof MISSED_SPACING_ERROR_ID
|
||||
| typeof EXTRA_SPACING_ERROR_ID
|
||||
| typeof GROUP_ORDER_ERROR_ID
|
||||
| typeof ORDER_ERROR_ID
|
||||
export declare const ORDER_ERROR_ID = 'unexpectedHeritageClausesOrder'
|
||||
export declare const GROUP_ORDER_ERROR_ID =
|
||||
'unexpectedHeritageClausesGroupOrder'
|
||||
export declare const EXTRA_SPACING_ERROR_ID =
|
||||
'extraSpacingBetweenHeritageClauses'
|
||||
export declare const MISSED_SPACING_ERROR_ID =
|
||||
'missedSpacingBetweenHeritageClauses'
|
||||
export type Options = Partial<
|
||||
{
|
||||
/**
|
||||
* Conditional configuration based on pattern matching.
|
||||
*/
|
||||
useConfigurationIf: {
|
||||
/**
|
||||
* Regular expression pattern to match against all heritage clause
|
||||
* names. The rule is only applied when all names match this pattern.
|
||||
*/
|
||||
allNamesMatchPattern?: RegexOption
|
||||
/**
|
||||
* AST selector to match against ClassDeclaration or
|
||||
* TSInterfaceDeclaration nodes.
|
||||
*/
|
||||
matchesAstSelector?: string
|
||||
}
|
||||
} & AllCommonOptions<
|
||||
TypeOption,
|
||||
AdditionalSortOptions,
|
||||
CustomGroupMatchOptions
|
||||
>
|
||||
>[]
|
||||
/**
|
||||
* Match options for a custom group.
|
||||
*/
|
||||
type CustomGroupMatchOptions = object
|
||||
type AdditionalSortOptions = object
|
||||
export {}
|
||||
Generated
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
var ORDER_ERROR_ID = 'unexpectedHeritageClausesOrder'
|
||||
var GROUP_ORDER_ERROR_ID = 'unexpectedHeritageClausesGroupOrder'
|
||||
var EXTRA_SPACING_ERROR_ID = 'extraSpacingBetweenHeritageClauses'
|
||||
var MISSED_SPACING_ERROR_ID = 'missedSpacingBetweenHeritageClauses'
|
||||
export {
|
||||
EXTRA_SPACING_ERROR_ID,
|
||||
GROUP_ORDER_ERROR_ID,
|
||||
MISSED_SPACING_ERROR_ID,
|
||||
ORDER_ERROR_ID,
|
||||
}
|
||||
Generated
Vendored
+23
@@ -0,0 +1,23 @@
|
||||
import { JSONSchema4 } from '@typescript-eslint/utils/json-schema'
|
||||
import { Options } from './sort-import-attributes/types.js'
|
||||
declare const ORDER_ERROR_ID = 'unexpectedImportAttributesOrder'
|
||||
declare const GROUP_ORDER_ERROR_ID = 'unexpectedImportAttributesGroupOrder'
|
||||
declare const EXTRA_SPACING_ERROR_ID = 'extraSpacingBetweenImportAttributes'
|
||||
declare const MISSED_SPACING_ERROR_ID = 'missedSpacingBetweenImportAttributes'
|
||||
type MessageId =
|
||||
| typeof MISSED_SPACING_ERROR_ID
|
||||
| typeof EXTRA_SPACING_ERROR_ID
|
||||
| typeof GROUP_ORDER_ERROR_ID
|
||||
| typeof ORDER_ERROR_ID
|
||||
export declare let jsonSchema: JSONSchema4
|
||||
declare const _default: import('@typescript-eslint/utils/ts-eslint').RuleModule<
|
||||
MessageId,
|
||||
Options,
|
||||
{
|
||||
recommended?: boolean
|
||||
},
|
||||
import('@typescript-eslint/utils/ts-eslint').RuleListener
|
||||
> & {
|
||||
name: string
|
||||
}
|
||||
export default _default
|
||||
Generated
Vendored
+100
@@ -0,0 +1,100 @@
|
||||
import {
|
||||
buildCommonJsonSchemas,
|
||||
buildUseConfigurationIfJsonSchema,
|
||||
matchesAstSelectorJsonSchema,
|
||||
} from '../utils/json-schemas/common-json-schemas.js'
|
||||
import { buildCommonGroupsJsonSchemas } from '../utils/json-schemas/common-groups-json-schemas.js'
|
||||
import {
|
||||
EXTRA_SPACING_ERROR,
|
||||
GROUP_ORDER_ERROR,
|
||||
MISSED_SPACING_ERROR,
|
||||
ORDER_ERROR,
|
||||
} from '../utils/report-errors.js'
|
||||
import {
|
||||
partitionByCommentJsonSchema,
|
||||
partitionByNewLineJsonSchema,
|
||||
} from '../utils/json-schemas/common-partition-json-schemas.js'
|
||||
import { buildAstListeners } from '../utils/build-ast-listeners.js'
|
||||
import { createEslintRule } from '../utils/create-eslint-rule.js'
|
||||
import { sortImportOrExportAttributes } from './sort-import-attributes/sort-import-or-export-attributes.js'
|
||||
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
|
||||
var ORDER_ERROR_ID = 'unexpectedImportAttributesOrder'
|
||||
var GROUP_ORDER_ERROR_ID = 'unexpectedImportAttributesGroupOrder'
|
||||
var EXTRA_SPACING_ERROR_ID = 'extraSpacingBetweenImportAttributes'
|
||||
var MISSED_SPACING_ERROR_ID = 'missedSpacingBetweenImportAttributes'
|
||||
var defaultOptions = {
|
||||
fallbackSort: { type: 'unsorted' },
|
||||
newlinesInside: 'newlinesBetween',
|
||||
specialCharacters: 'keep',
|
||||
partitionByComment: false,
|
||||
partitionByNewLine: false,
|
||||
newlinesBetween: 'ignore',
|
||||
useConfigurationIf: {},
|
||||
type: 'alphabetical',
|
||||
ignoreCase: true,
|
||||
customGroups: [],
|
||||
locales: 'en-US',
|
||||
alphabet: '',
|
||||
order: 'asc',
|
||||
groups: [],
|
||||
}
|
||||
var jsonSchema = {
|
||||
items: {
|
||||
properties: {
|
||||
...buildCommonJsonSchemas(),
|
||||
...buildCommonGroupsJsonSchemas(),
|
||||
useConfigurationIf: buildUseConfigurationIfJsonSchema({
|
||||
additionalProperties: {
|
||||
matchesAstSelector: matchesAstSelectorJsonSchema,
|
||||
},
|
||||
}),
|
||||
partitionByComment: partitionByCommentJsonSchema,
|
||||
partitionByNewLine: partitionByNewLineJsonSchema,
|
||||
},
|
||||
additionalProperties: false,
|
||||
type: 'object',
|
||||
},
|
||||
uniqueItems: true,
|
||||
type: 'array',
|
||||
}
|
||||
var sort_import_attributes_default = createEslintRule({
|
||||
meta: {
|
||||
messages: {
|
||||
[MISSED_SPACING_ERROR_ID]: MISSED_SPACING_ERROR,
|
||||
[EXTRA_SPACING_ERROR_ID]: EXTRA_SPACING_ERROR,
|
||||
[GROUP_ORDER_ERROR_ID]: GROUP_ORDER_ERROR,
|
||||
[ORDER_ERROR_ID]: ORDER_ERROR,
|
||||
},
|
||||
docs: {
|
||||
url: 'https://perfectionist.dev/rules/sort-import-attributes',
|
||||
description: 'Enforce sorted import attributes.',
|
||||
recommended: true,
|
||||
},
|
||||
schema: jsonSchema,
|
||||
type: 'suggestion',
|
||||
fixable: 'code',
|
||||
},
|
||||
create: context =>
|
||||
buildAstListeners({
|
||||
nodeTypes: [AST_NODE_TYPES.ImportDeclaration],
|
||||
sorter: sortImportAttributes,
|
||||
context,
|
||||
}),
|
||||
defaultOptions: [defaultOptions],
|
||||
name: 'sort-import-attributes',
|
||||
})
|
||||
function sortImportAttributes({ matchedAstSelectors, context, node }) {
|
||||
sortImportOrExportAttributes({
|
||||
availableMessageIds: {
|
||||
missedSpacingBetweenMembers: MISSED_SPACING_ERROR_ID,
|
||||
extraSpacingBetweenMembers: EXTRA_SPACING_ERROR_ID,
|
||||
unexpectedGroupOrder: GROUP_ORDER_ERROR_ID,
|
||||
unexpectedOrder: ORDER_ERROR_ID,
|
||||
},
|
||||
matchedAstSelectors,
|
||||
defaultOptions,
|
||||
context,
|
||||
node,
|
||||
})
|
||||
}
|
||||
export { sort_import_attributes_default as default, jsonSchema }
|
||||
Generated
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
import { RuleContext } from '@typescript-eslint/utils/ts-eslint'
|
||||
import { TSESTree } from '@typescript-eslint/types'
|
||||
import { Options } from './types.js'
|
||||
/**
|
||||
* Computes the matched context options for a given import/export attributes
|
||||
* node.
|
||||
*
|
||||
* @param params - Parameters.
|
||||
* @param params.matchedAstSelectors - The matched AST selectors for an
|
||||
* import/export declaration node.
|
||||
* @param params.attributes - The import attributes to compute the context
|
||||
* options for.
|
||||
* @param params.context - The rule context.
|
||||
* @returns The matched context options or undefined if none match.
|
||||
*/
|
||||
export declare function computeMatchedContextOptions<
|
||||
MessageIds extends string,
|
||||
>({
|
||||
matchedAstSelectors,
|
||||
attributes,
|
||||
context,
|
||||
}: {
|
||||
context: Readonly<RuleContext<MessageIds, Options>>
|
||||
matchedAstSelectors: ReadonlySet<string>
|
||||
attributes: TSESTree.ImportAttribute[]
|
||||
}): Options[number] | undefined
|
||||
Generated
Vendored
+47
@@ -0,0 +1,47 @@
|
||||
import { passesAllNamesMatchPatternFilter } from '../../utils/context-matching/passes-all-names-match-pattern-filter.js'
|
||||
import { passesAstSelectorFilter } from '../../utils/context-matching/passes-ast-selector-filter.js'
|
||||
import { computeNodeName } from './compute-node-name.js'
|
||||
/**
|
||||
* Computes the matched context options for a given import/export attributes
|
||||
* node.
|
||||
*
|
||||
* @param params - Parameters.
|
||||
* @param params.matchedAstSelectors - The matched AST selectors for an
|
||||
* import/export declaration node.
|
||||
* @param params.attributes - The import attributes to compute the context
|
||||
* options for.
|
||||
* @param params.context - The rule context.
|
||||
* @returns The matched context options or undefined if none match.
|
||||
*/
|
||||
function computeMatchedContextOptions({
|
||||
matchedAstSelectors,
|
||||
attributes,
|
||||
context,
|
||||
}) {
|
||||
let nodeNames = attributes.map(attribute =>
|
||||
computeNodeName(attribute, context.sourceCode),
|
||||
)
|
||||
return context.options.find(options =>
|
||||
isContextOptionMatching({
|
||||
matchedAstSelectors,
|
||||
nodeNames,
|
||||
options,
|
||||
}),
|
||||
)
|
||||
}
|
||||
function isContextOptionMatching({ matchedAstSelectors, nodeNames, options }) {
|
||||
if (!options.useConfigurationIf) {
|
||||
return true
|
||||
}
|
||||
return (
|
||||
passesAllNamesMatchPatternFilter({
|
||||
allNamesMatchPattern: options.useConfigurationIf.allNamesMatchPattern,
|
||||
nodeNames,
|
||||
}) &&
|
||||
passesAstSelectorFilter({
|
||||
matchesAstSelector: options.useConfigurationIf.matchesAstSelector,
|
||||
matchedAstSelectors,
|
||||
})
|
||||
)
|
||||
}
|
||||
export { computeMatchedContextOptions }
|
||||
Generated
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
import { TSESLint } from '@typescript-eslint/utils'
|
||||
import { TSESTree } from '@typescript-eslint/types'
|
||||
/**
|
||||
* Extracts the name of an import attribute for sorting purposes.
|
||||
*
|
||||
* For identifier keys, returns the identifier name. For literal keys, returns
|
||||
* the string value. Falls back to source code text if needed.
|
||||
*
|
||||
* @param attribute - The import attribute AST node.
|
||||
* @param sourceCode - The ESLint source code object.
|
||||
* @returns The attribute name to use for sorting.
|
||||
*/
|
||||
export declare function computeNodeName(
|
||||
attribute: TSESTree.ImportAttribute,
|
||||
sourceCode: TSESLint.SourceCode,
|
||||
): string
|
||||
Generated
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
import { UnreachableCaseError } from '../../utils/unreachable-case-error.js'
|
||||
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
|
||||
/**
|
||||
* Extracts the name of an import attribute for sorting purposes.
|
||||
*
|
||||
* For identifier keys, returns the identifier name. For literal keys, returns
|
||||
* the string value. Falls back to source code text if needed.
|
||||
*
|
||||
* @param attribute - The import attribute AST node.
|
||||
* @param sourceCode - The ESLint source code object.
|
||||
* @returns The attribute name to use for sorting.
|
||||
*/
|
||||
function computeNodeName(attribute, sourceCode) {
|
||||
let { key } = attribute
|
||||
switch (key.type) {
|
||||
case AST_NODE_TYPES.Identifier:
|
||||
return key.name
|
||||
case AST_NODE_TYPES.Literal:
|
||||
return key.value?.toString() ?? sourceCode.getText(attribute)
|
||||
/* v8 ignore next 2 -- @preserve Exhaustive guard. */
|
||||
default:
|
||||
throw new UnreachableCaseError(key)
|
||||
}
|
||||
}
|
||||
export { computeNodeName }
|
||||
Generated
Vendored
+23
@@ -0,0 +1,23 @@
|
||||
import { TSESTree } from '@typescript-eslint/types'
|
||||
import { TSESLint } from '@typescript-eslint/utils'
|
||||
import { Options } from './types.js'
|
||||
export declare function sortImportOrExportAttributes<
|
||||
MessageIds extends string,
|
||||
>({
|
||||
matchedAstSelectors,
|
||||
availableMessageIds,
|
||||
defaultOptions,
|
||||
context,
|
||||
node,
|
||||
}: {
|
||||
availableMessageIds: {
|
||||
missedSpacingBetweenMembers: MessageIds
|
||||
extraSpacingBetweenMembers: MessageIds
|
||||
unexpectedGroupOrder: MessageIds
|
||||
unexpectedOrder: MessageIds
|
||||
}
|
||||
node: TSESTree.ExportNamedDeclaration | TSESTree.ImportDeclaration
|
||||
context: TSESLint.RuleContext<MessageIds, Options>
|
||||
defaultOptions: Required<Options[number]>
|
||||
matchedAstSelectors: ReadonlySet<string>
|
||||
}): void
|
||||
Generated
Vendored
+112
@@ -0,0 +1,112 @@
|
||||
import { validateNewlinesAndPartitionConfiguration } from '../../utils/validate-newlines-and-partition-configuration.js'
|
||||
import { defaultComparatorByOptionsComputer } from '../../utils/compare/default-comparator-by-options-computer.js'
|
||||
import { buildOptionsByGroupIndexComputer } from '../../utils/build-options-by-group-index-computer.js'
|
||||
import { validateCustomSortConfiguration } from '../../utils/validate-custom-sort-configuration.js'
|
||||
import { validateGroupsConfiguration } from '../../utils/validate-groups-configuration.js'
|
||||
import { getEslintDisabledLines } from '../../utils/get-eslint-disabled-lines.js'
|
||||
import { doesCustomGroupMatch } from '../../utils/does-custom-group-match.js'
|
||||
import { isNodeEslintDisabled } from '../../utils/is-node-eslint-disabled.js'
|
||||
import { sortNodesByGroups } from '../../utils/sort-nodes-by-groups.js'
|
||||
import { reportAllErrors } from '../../utils/report-all-errors.js'
|
||||
import { shouldPartition } from '../../utils/should-partition.js'
|
||||
import { computeGroup } from '../../utils/compute-group.js'
|
||||
import { rangeToDiff } from '../../utils/range-to-diff.js'
|
||||
import { getSettings } from '../../utils/get-settings.js'
|
||||
import { isSortable } from '../../utils/is-sortable.js'
|
||||
import { complete } from '../../utils/complete.js'
|
||||
import { computeNodeName } from './compute-node-name.js'
|
||||
import { computeMatchedContextOptions } from './compute-matched-context-options.js'
|
||||
function sortImportOrExportAttributes({
|
||||
matchedAstSelectors,
|
||||
availableMessageIds,
|
||||
defaultOptions,
|
||||
context,
|
||||
node,
|
||||
}) {
|
||||
let attributes = node.attributes
|
||||
if (!isSortable(attributes)) {
|
||||
return
|
||||
}
|
||||
let { sourceCode, id } = context
|
||||
let settings = getSettings(context.settings)
|
||||
let options = complete(
|
||||
computeMatchedContextOptions({
|
||||
matchedAstSelectors,
|
||||
attributes,
|
||||
context,
|
||||
}),
|
||||
settings,
|
||||
defaultOptions,
|
||||
)
|
||||
validateCustomSortConfiguration(options)
|
||||
validateGroupsConfiguration({
|
||||
selectors: [],
|
||||
modifiers: [],
|
||||
options,
|
||||
})
|
||||
validateNewlinesAndPartitionConfiguration(options)
|
||||
let eslintDisabledLines = getEslintDisabledLines({
|
||||
ruleName: id,
|
||||
sourceCode,
|
||||
})
|
||||
let optionsByGroupIndexComputer = buildOptionsByGroupIndexComputer(options)
|
||||
let formattedMembers = [[]]
|
||||
for (let attribute of attributes) {
|
||||
let name = computeNodeName(attribute, sourceCode)
|
||||
let group = computeGroup({
|
||||
customGroupMatcher: customGroup =>
|
||||
doesCustomGroupMatch({
|
||||
elementName: name,
|
||||
selectors: [],
|
||||
modifiers: [],
|
||||
customGroup,
|
||||
}),
|
||||
predefinedGroups: [],
|
||||
options,
|
||||
})
|
||||
let sortingNode = {
|
||||
isEslintDisabled: isNodeEslintDisabled(attribute, eslintDisabledLines),
|
||||
size: rangeToDiff(attribute, sourceCode),
|
||||
node: attribute,
|
||||
group,
|
||||
name,
|
||||
}
|
||||
let lastSortingNode = formattedMembers.at(-1)?.at(-1)
|
||||
if (
|
||||
shouldPartition({
|
||||
lastSortingNode,
|
||||
sortingNode,
|
||||
sourceCode,
|
||||
options,
|
||||
})
|
||||
) {
|
||||
formattedMembers.push([])
|
||||
}
|
||||
formattedMembers.at(-1).push({
|
||||
...sortingNode,
|
||||
partitionId: formattedMembers.length,
|
||||
})
|
||||
}
|
||||
for (let nodes of formattedMembers) {
|
||||
function createSortNodesExcludingEslintDisabled(sortingNodes) {
|
||||
return function (ignoreEslintDisabledNodes) {
|
||||
return sortNodesByGroups({
|
||||
comparatorByOptionsComputer: defaultComparatorByOptionsComputer,
|
||||
optionsByGroupIndexComputer,
|
||||
ignoreEslintDisabledNodes,
|
||||
groups: options.groups,
|
||||
nodes: sortingNodes,
|
||||
})
|
||||
}
|
||||
}
|
||||
reportAllErrors({
|
||||
sortNodesExcludingEslintDisabled:
|
||||
createSortNodesExcludingEslintDisabled(nodes),
|
||||
availableMessageIds,
|
||||
options,
|
||||
context,
|
||||
nodes,
|
||||
})
|
||||
}
|
||||
}
|
||||
export { sortImportOrExportAttributes }
|
||||
Generated
Vendored
+35
@@ -0,0 +1,35 @@
|
||||
import { TSESTree } from '@typescript-eslint/types'
|
||||
import { RegexOption, TypeOption } from '../../types/common-options.js'
|
||||
import { AllCommonOptions } from '../../types/all-common-options.js'
|
||||
import { SortingNode } from '../../types/sorting-node.js'
|
||||
export type Options = Partial<
|
||||
{
|
||||
/**
|
||||
* Conditional configuration based on pattern matching.
|
||||
*/
|
||||
useConfigurationIf: {
|
||||
/**
|
||||
* Regular expression pattern to match against all attribute key names.
|
||||
* The rule is only applied when all names match this pattern.
|
||||
*/
|
||||
allNamesMatchPattern?: RegexOption
|
||||
/**
|
||||
* AST selector to match against ImportDeclaration or
|
||||
* ExportNamedDeclaration nodes.
|
||||
*/
|
||||
matchesAstSelector?: string
|
||||
}
|
||||
} & AllCommonOptions<
|
||||
TypeOption,
|
||||
AdditionalSortOptions,
|
||||
CustomGroupMatchOptions
|
||||
>
|
||||
>[]
|
||||
export type SortImportAttributesSortingNode =
|
||||
SortingNode<TSESTree.ImportAttribute>
|
||||
/**
|
||||
* Match options for a custom group.
|
||||
*/
|
||||
type CustomGroupMatchOptions = object
|
||||
type AdditionalSortOptions = object
|
||||
export {}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
import { TSESLint } from '@typescript-eslint/utils'
|
||||
import { Options } from './sort-imports/types.js'
|
||||
declare const ORDER_ERROR_ID = 'unexpectedImportsOrder'
|
||||
declare const GROUP_ORDER_ERROR_ID = 'unexpectedImportsGroupOrder'
|
||||
declare const EXTRA_SPACING_ERROR_ID = 'extraSpacingBetweenImports'
|
||||
declare const MISSED_SPACING_ERROR_ID = 'missedSpacingBetweenImports'
|
||||
declare const MISSED_COMMENT_ABOVE_ERROR_ID = 'missedCommentAboveImport'
|
||||
declare const DEPENDENCY_ORDER_ERROR_ID = 'unexpectedImportsDependencyOrder'
|
||||
export type MessageId =
|
||||
| typeof MISSED_COMMENT_ABOVE_ERROR_ID
|
||||
| typeof DEPENDENCY_ORDER_ERROR_ID
|
||||
| typeof MISSED_SPACING_ERROR_ID
|
||||
| typeof EXTRA_SPACING_ERROR_ID
|
||||
| typeof GROUP_ORDER_ERROR_ID
|
||||
| typeof ORDER_ERROR_ID
|
||||
declare const _default: TSESLint.RuleModule<
|
||||
MessageId,
|
||||
Options,
|
||||
{
|
||||
recommended?: boolean
|
||||
},
|
||||
TSESLint.RuleListener
|
||||
> & {
|
||||
name: string
|
||||
}
|
||||
export default _default
|
||||
+457
@@ -0,0 +1,457 @@
|
||||
import {
|
||||
buildCommonJsonSchemas,
|
||||
buildRegexJsonSchema,
|
||||
useExperimentalDependencyDetectionJsonSchema,
|
||||
} from '../utils/json-schemas/common-json-schemas.js'
|
||||
import { buildCommonGroupsJsonSchemas } from '../utils/json-schemas/common-groups-json-schemas.js'
|
||||
import {
|
||||
DEPENDENCY_ORDER_ERROR,
|
||||
EXTRA_SPACING_ERROR,
|
||||
GROUP_ORDER_ERROR,
|
||||
MISSED_COMMENT_ABOVE_ERROR,
|
||||
MISSED_SPACING_ERROR,
|
||||
ORDER_ERROR,
|
||||
} from '../utils/report-errors.js'
|
||||
import {
|
||||
partitionByCommentJsonSchema,
|
||||
partitionByNewLineJsonSchema,
|
||||
} from '../utils/json-schemas/common-partition-json-schemas.js'
|
||||
import { computeDependenciesBySortingNode } from '../utils/compute-dependencies-by-sorting-node.js'
|
||||
import { populateSortingNodeGroupsWithDependencies } from '../utils/populate-sorting-node-groups-with-dependencies.js'
|
||||
import { validateNewlinesAndPartitionConfiguration } from '../utils/validate-newlines-and-partition-configuration.js'
|
||||
import { buildOptionsByGroupIndexComputer } from '../utils/build-options-by-group-index-computer.js'
|
||||
import { validateCustomSortConfiguration } from '../utils/validate-custom-sort-configuration.js'
|
||||
import { validateGroupsConfiguration } from '../utils/validate-groups-configuration.js'
|
||||
import { generatePredefinedGroups } from '../utils/generate-predefined-groups.js'
|
||||
import { sortNodesByDependencies } from '../utils/sort-nodes-by-dependencies.js'
|
||||
import { getEslintDisabledLines } from '../utils/get-eslint-disabled-lines.js'
|
||||
import { doesCustomGroupMatch } from '../utils/does-custom-group-match.js'
|
||||
import { isNodeEslintDisabled } from '../utils/is-node-eslint-disabled.js'
|
||||
import { sortNodesByGroups } from '../utils/sort-nodes-by-groups.js'
|
||||
import { reportAllErrors } from '../utils/report-all-errors.js'
|
||||
import { shouldPartition } from '../utils/should-partition.js'
|
||||
import { computeGroup } from '../utils/compute-group.js'
|
||||
import { rangeToDiff } from '../utils/range-to-diff.js'
|
||||
import { getSettings } from '../utils/get-settings.js'
|
||||
import { isSortable } from '../utils/is-sortable.js'
|
||||
import { complete } from '../utils/complete.js'
|
||||
import { createEslintRule } from '../utils/create-eslint-rule.js'
|
||||
import { isNodeOnSingleLine } from '../utils/is-node-on-single-line.js'
|
||||
import {
|
||||
TYPE_IMPORT_FIRST_TYPE_OPTION,
|
||||
additionalCustomGroupMatchOptionsJsonSchema,
|
||||
additionalSortOptionsJsonSchema,
|
||||
allModifiers,
|
||||
allSelectors,
|
||||
} from './sort-imports/types.js'
|
||||
import { isNonExternalReferenceTsImportEquals } from './sort-imports/is-non-external-reference-ts-import-equals.js'
|
||||
import { isSideEffectOnlyGroup } from './sort-imports/is-side-effect-only-group.js'
|
||||
import { validateSideEffectsConfiguration } from './sort-imports/validate-side-effects-configuration.js'
|
||||
import { comparatorByOptionsComputer } from './sort-imports/comparator-by-options-computer.js'
|
||||
import { readClosestTsConfigByPath } from './sort-imports/read-closest-ts-config-by-path.js'
|
||||
import { computeSpecifierModifiers } from './sort-imports/compute-specifier-modifiers.js'
|
||||
import { getOptionsWithCleanGroups } from '../utils/get-options-with-clean-groups.js'
|
||||
import { computeCommonSelectors } from './sort-imports/compute-common-selectors.js'
|
||||
import { computeDependencyNames } from './sort-imports/compute-dependency-names.js'
|
||||
import { computeSpecifierName } from './sort-imports/compute-specifier-name.js'
|
||||
import { computeDependencies } from './sort-imports/compute-dependencies.js'
|
||||
import { isSideEffectImport } from './sort-imports/is-side-effect-import.js'
|
||||
import { computeNodeName } from './sort-imports/compute-node-name.js'
|
||||
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
|
||||
/**
|
||||
* Cache computed groups by modifiers and selectors for performance.
|
||||
*/
|
||||
var cachedGroupsByModifiersAndSelectors = /* @__PURE__ */ new Map()
|
||||
var ORDER_ERROR_ID = 'unexpectedImportsOrder'
|
||||
var GROUP_ORDER_ERROR_ID = 'unexpectedImportsGroupOrder'
|
||||
var EXTRA_SPACING_ERROR_ID = 'extraSpacingBetweenImports'
|
||||
var MISSED_SPACING_ERROR_ID = 'missedSpacingBetweenImports'
|
||||
var MISSED_COMMENT_ABOVE_ERROR_ID = 'missedCommentAboveImport'
|
||||
var DEPENDENCY_ORDER_ERROR_ID = 'unexpectedImportsDependencyOrder'
|
||||
var defaultOptions = {
|
||||
groups: [
|
||||
'type-import',
|
||||
['value-builtin', 'value-external'],
|
||||
'type-internal',
|
||||
'value-internal',
|
||||
['type-parent', 'type-sibling', 'type-index'],
|
||||
['value-parent', 'value-sibling', 'value-index'],
|
||||
'ts-equals-import',
|
||||
'unknown',
|
||||
],
|
||||
internalPattern: ['^~/.+', '^@/.+', '^#.+'],
|
||||
useExperimentalDependencyDetection: true,
|
||||
fallbackSort: { type: 'unsorted' },
|
||||
partitionByComment: false,
|
||||
partitionByNewLine: false,
|
||||
specialCharacters: 'keep',
|
||||
tsconfig: { rootDir: '' },
|
||||
maxLineLength: Infinity,
|
||||
sortSideEffects: false,
|
||||
type: 'alphabetical',
|
||||
environment: 'node',
|
||||
newlinesBetween: 1,
|
||||
newlinesInside: 0,
|
||||
customGroups: [],
|
||||
ignoreCase: true,
|
||||
locales: 'en-US',
|
||||
sortBy: 'path',
|
||||
alphabet: '',
|
||||
order: 'asc',
|
||||
}
|
||||
var sort_imports_default = createEslintRule({
|
||||
create: context => {
|
||||
let settings = getSettings(context.settings)
|
||||
let options = getOptionsWithCleanGroups(
|
||||
complete(context.options.at(0), settings, defaultOptions),
|
||||
)
|
||||
validateGroupsConfiguration({
|
||||
selectors: allSelectors,
|
||||
modifiers: allModifiers,
|
||||
options,
|
||||
})
|
||||
validateCustomSortConfiguration(options)
|
||||
validateNewlinesAndPartitionConfiguration(options)
|
||||
validateSideEffectsConfiguration(options)
|
||||
let tsconfigRootDirectory = options.tsconfig.rootDir
|
||||
let tsConfigOutput =
|
||||
tsconfigRootDirectory ?
|
||||
readClosestTsConfigByPath({
|
||||
tsconfigFilename: options.tsconfig.filename ?? 'tsconfig.json',
|
||||
tsconfigRootDir: tsconfigRootDirectory,
|
||||
filePath: context.physicalFilename,
|
||||
contextCwd: context.cwd,
|
||||
})
|
||||
: null
|
||||
let { sourceCode, filename, id } = context
|
||||
let eslintDisabledLines = getEslintDisabledLines({
|
||||
ruleName: id,
|
||||
sourceCode,
|
||||
})
|
||||
let sortingNodesWithoutPartitionId = []
|
||||
let flatGroups = new Set(options.groups.flat())
|
||||
let shouldRegroupSideEffectNodes = flatGroups.has('side-effect')
|
||||
let shouldRegroupSideEffectStyleNodes = flatGroups.has('side-effect-style')
|
||||
function registerNode(node) {
|
||||
let name = computeNodeName({
|
||||
sourceCode,
|
||||
node,
|
||||
})
|
||||
let commonSelectors = computeCommonSelectors({
|
||||
tsConfigOutput,
|
||||
filename,
|
||||
options,
|
||||
name,
|
||||
})
|
||||
let selectors = []
|
||||
let modifiers = []
|
||||
let group = null
|
||||
if (
|
||||
node.type !== AST_NODE_TYPES.VariableDeclaration &&
|
||||
node.importKind === 'type'
|
||||
) {
|
||||
selectors.push('type')
|
||||
modifiers.push('type')
|
||||
}
|
||||
let isSideEffect = isSideEffectImport({
|
||||
sourceCode,
|
||||
node,
|
||||
})
|
||||
let isStyleValue = isStyle(name)
|
||||
let isStyleSideEffect = isSideEffect && isStyleValue
|
||||
if (!isNonExternalReferenceTsImportEquals(node)) {
|
||||
if (isStyleSideEffect) {
|
||||
selectors.push('side-effect-style')
|
||||
}
|
||||
if (isSideEffect) {
|
||||
selectors.push('side-effect')
|
||||
modifiers.push('side-effect')
|
||||
}
|
||||
if (isStyleValue) {
|
||||
selectors.push('style')
|
||||
}
|
||||
for (let selector of commonSelectors) {
|
||||
selectors.push(selector)
|
||||
}
|
||||
}
|
||||
selectors.push('import')
|
||||
if (!modifiers.includes('type')) {
|
||||
modifiers.push('value')
|
||||
}
|
||||
if (node.type === AST_NODE_TYPES.TSImportEqualsDeclaration) {
|
||||
modifiers.push('ts-equals')
|
||||
}
|
||||
if (node.type === AST_NODE_TYPES.VariableDeclaration) {
|
||||
modifiers.push('require')
|
||||
}
|
||||
modifiers.push(...computeSpecifierModifiers(node))
|
||||
if (isNodeOnSingleLine(node)) {
|
||||
modifiers.push('singleline')
|
||||
} else {
|
||||
modifiers.push('multiline')
|
||||
}
|
||||
group ??=
|
||||
computeGroupExceptUnknown({
|
||||
selectors,
|
||||
modifiers,
|
||||
options,
|
||||
name,
|
||||
}) ?? 'unknown'
|
||||
let hasMultipleImportDeclarations =
|
||||
node.type === AST_NODE_TYPES.ImportDeclaration &&
|
||||
isSortable(node.specifiers)
|
||||
let size = rangeToDiff(node, sourceCode)
|
||||
if (hasMultipleImportDeclarations && size > options.maxLineLength) {
|
||||
size = name.length + 10
|
||||
}
|
||||
sortingNodesWithoutPartitionId.push({
|
||||
isIgnored:
|
||||
!options.sortSideEffects &&
|
||||
isSideEffect &&
|
||||
!shouldRegroupSideEffectNodes &&
|
||||
(!isStyleSideEffect || !shouldRegroupSideEffectStyleNodes),
|
||||
dependencies:
|
||||
options.useExperimentalDependencyDetection ?
|
||||
[]
|
||||
: computeDependencies(node),
|
||||
isEslintDisabled: isNodeEslintDisabled(node, eslintDisabledLines),
|
||||
dependencyNames: computeDependencyNames({
|
||||
sourceCode,
|
||||
node,
|
||||
}),
|
||||
specifierName: computeSpecifierName({
|
||||
sourceCode,
|
||||
node,
|
||||
}),
|
||||
isTypeImport: modifiers.includes('type'),
|
||||
addSafetySemicolonWhenInline: true,
|
||||
group,
|
||||
size,
|
||||
name,
|
||||
node,
|
||||
})
|
||||
}
|
||||
return {
|
||||
VariableDeclaration: node => {
|
||||
if (
|
||||
node.declarations[0].init?.type === AST_NODE_TYPES.CallExpression &&
|
||||
node.declarations[0].init.callee.type === AST_NODE_TYPES.Identifier &&
|
||||
node.declarations[0].init.callee.name === 'require' &&
|
||||
node.declarations[0].init.arguments[0]?.type ===
|
||||
AST_NODE_TYPES.Literal
|
||||
) {
|
||||
registerNode(node)
|
||||
}
|
||||
},
|
||||
'Program:exit': () => {
|
||||
sortImportNodes({
|
||||
sortingNodesWithoutPartitionId,
|
||||
context,
|
||||
options,
|
||||
})
|
||||
},
|
||||
TSImportEqualsDeclaration: registerNode,
|
||||
ImportDeclaration: registerNode,
|
||||
}
|
||||
},
|
||||
meta: {
|
||||
schema: {
|
||||
items: {
|
||||
properties: {
|
||||
...buildCommonJsonSchemas({
|
||||
allowedAdditionalTypeValues: [TYPE_IMPORT_FIRST_TYPE_OPTION],
|
||||
additionalSortProperties: additionalSortOptionsJsonSchema,
|
||||
}),
|
||||
...buildCommonGroupsJsonSchemas({
|
||||
additionalCustomGroupMatchProperties:
|
||||
additionalCustomGroupMatchOptionsJsonSchema,
|
||||
allowedAdditionalTypeValues: [TYPE_IMPORT_FIRST_TYPE_OPTION],
|
||||
additionalSortProperties: additionalSortOptionsJsonSchema,
|
||||
}),
|
||||
tsconfig: {
|
||||
properties: {
|
||||
rootDir: {
|
||||
description: 'Specifies the tsConfig root directory.',
|
||||
type: 'string',
|
||||
},
|
||||
filename: {
|
||||
description: 'Specifies the tsConfig filename.',
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
required: ['rootDir'],
|
||||
type: 'object',
|
||||
},
|
||||
maxLineLength: {
|
||||
description: 'Specifies the maximum line length.',
|
||||
exclusiveMinimum: true,
|
||||
type: 'integer',
|
||||
minimum: 0,
|
||||
},
|
||||
sortSideEffects: {
|
||||
description:
|
||||
'Controls whether side-effect imports should be sorted.',
|
||||
type: 'boolean',
|
||||
},
|
||||
environment: {
|
||||
description: 'Specifies the environment.',
|
||||
enum: ['node', 'bun'],
|
||||
type: 'string',
|
||||
},
|
||||
useExperimentalDependencyDetection:
|
||||
useExperimentalDependencyDetectionJsonSchema,
|
||||
partitionByComment: partitionByCommentJsonSchema,
|
||||
partitionByNewLine: partitionByNewLineJsonSchema,
|
||||
internalPattern: buildRegexJsonSchema(),
|
||||
},
|
||||
additionalProperties: false,
|
||||
type: 'object',
|
||||
},
|
||||
uniqueItems: true,
|
||||
type: 'array',
|
||||
},
|
||||
messages: {
|
||||
[MISSED_COMMENT_ABOVE_ERROR_ID]: MISSED_COMMENT_ABOVE_ERROR,
|
||||
[DEPENDENCY_ORDER_ERROR_ID]: DEPENDENCY_ORDER_ERROR,
|
||||
[MISSED_SPACING_ERROR_ID]: MISSED_SPACING_ERROR,
|
||||
[EXTRA_SPACING_ERROR_ID]: EXTRA_SPACING_ERROR,
|
||||
[GROUP_ORDER_ERROR_ID]: GROUP_ORDER_ERROR,
|
||||
[ORDER_ERROR_ID]: ORDER_ERROR,
|
||||
},
|
||||
docs: {
|
||||
url: 'https://perfectionist.dev/rules/sort-imports',
|
||||
description: 'Enforce sorted imports.',
|
||||
recommended: true,
|
||||
},
|
||||
type: 'suggestion',
|
||||
fixable: 'code',
|
||||
},
|
||||
defaultOptions: [defaultOptions],
|
||||
name: 'sort-imports',
|
||||
})
|
||||
function sortImportNodes({ sortingNodesWithoutPartitionId, options, context }) {
|
||||
let { sourceCode } = context
|
||||
let optionsByGroupIndexComputer = buildOptionsByGroupIndexComputer(options)
|
||||
let contentSeparatedSortingNodeGroups = [[[]]]
|
||||
for (let sortingNodeWithoutPartitionId of sortingNodesWithoutPartitionId) {
|
||||
let lastGroupWithNoContentBetween = contentSeparatedSortingNodeGroups.at(-1)
|
||||
let lastGroup = lastGroupWithNoContentBetween.at(-1)
|
||||
let lastSortingNode = lastGroup.at(-1)
|
||||
if (
|
||||
lastSortingNode &&
|
||||
hasContentBetweenNodes(lastSortingNode, sortingNodeWithoutPartitionId)
|
||||
) {
|
||||
lastGroup = []
|
||||
lastGroupWithNoContentBetween = [lastGroup]
|
||||
contentSeparatedSortingNodeGroups.push(lastGroupWithNoContentBetween)
|
||||
} else if (
|
||||
shouldPartition({
|
||||
sortingNode: sortingNodeWithoutPartitionId,
|
||||
lastSortingNode,
|
||||
sourceCode,
|
||||
options,
|
||||
})
|
||||
) {
|
||||
lastGroup = []
|
||||
lastGroupWithNoContentBetween.push(lastGroup)
|
||||
}
|
||||
lastGroup.push({
|
||||
...sortingNodeWithoutPartitionId,
|
||||
partitionId: lastGroupWithNoContentBetween.length,
|
||||
})
|
||||
}
|
||||
for (let contentSeparatedSortingNodeGroup of contentSeparatedSortingNodeGroups) {
|
||||
let sortingNodeGroups = [...contentSeparatedSortingNodeGroup]
|
||||
if (options.useExperimentalDependencyDetection) {
|
||||
sortingNodeGroups = populateSortingNodeGroupsWithDependencies({
|
||||
dependenciesBySortingNode: computeDependenciesBySortingNode({
|
||||
sortingNodes: sortingNodeGroups.flat(),
|
||||
sourceCode,
|
||||
}),
|
||||
sortingNodeGroups,
|
||||
})
|
||||
}
|
||||
let sortingNodes = sortingNodeGroups.flat()
|
||||
reportAllErrors({
|
||||
availableMessageIds: {
|
||||
unexpectedDependencyOrder: DEPENDENCY_ORDER_ERROR_ID,
|
||||
missedSpacingBetweenMembers: MISSED_SPACING_ERROR_ID,
|
||||
extraSpacingBetweenMembers: EXTRA_SPACING_ERROR_ID,
|
||||
missedCommentAbove: MISSED_COMMENT_ABOVE_ERROR_ID,
|
||||
unexpectedGroupOrder: GROUP_ORDER_ERROR_ID,
|
||||
unexpectedOrder: ORDER_ERROR_ID,
|
||||
},
|
||||
sortNodesExcludingEslintDisabled:
|
||||
createSortNodesExcludingEslintDisabled(sortingNodeGroups),
|
||||
nodes: sortingNodes,
|
||||
options,
|
||||
context,
|
||||
})
|
||||
}
|
||||
function createSortNodesExcludingEslintDisabled(nodeGroups) {
|
||||
return function (ignoreEslintDisabledNodes) {
|
||||
return sortNodesByDependencies(
|
||||
nodeGroups.flatMap(nodes =>
|
||||
sortNodesByGroups({
|
||||
isNodeIgnoredForGroup: ({ groupIndex }) => {
|
||||
if (options.sortSideEffects) {
|
||||
return false
|
||||
}
|
||||
return isSideEffectOnlyGroup(options.groups[groupIndex])
|
||||
},
|
||||
isNodeIgnored: node => node.isIgnored,
|
||||
optionsByGroupIndexComputer,
|
||||
comparatorByOptionsComputer,
|
||||
ignoreEslintDisabledNodes,
|
||||
groups: options.groups,
|
||||
nodes,
|
||||
}),
|
||||
),
|
||||
{ ignoreEslintDisabledNodes },
|
||||
)
|
||||
}
|
||||
}
|
||||
function hasContentBetweenNodes(left, right) {
|
||||
return (
|
||||
sourceCode.getTokensBetween(left.node, right.node, {
|
||||
includeComments: false,
|
||||
}).length > 0
|
||||
)
|
||||
}
|
||||
}
|
||||
function computeGroupExceptUnknown({ selectors, modifiers, options, name }) {
|
||||
let computedCustomGroup = computeGroup({
|
||||
customGroupMatcher: customGroup =>
|
||||
doesCustomGroupMatch({
|
||||
elementName: name,
|
||||
customGroup,
|
||||
modifiers,
|
||||
selectors,
|
||||
}),
|
||||
predefinedGroups: generatePredefinedGroups({
|
||||
cache: cachedGroupsByModifiersAndSelectors,
|
||||
selectors,
|
||||
modifiers,
|
||||
}),
|
||||
options,
|
||||
})
|
||||
if (computedCustomGroup === 'unknown') {
|
||||
return null
|
||||
}
|
||||
return computedCustomGroup
|
||||
}
|
||||
var styleExtensions = [
|
||||
'.less',
|
||||
'.scss',
|
||||
'.sass',
|
||||
'.styl',
|
||||
'.pcss',
|
||||
'.css',
|
||||
'.sss',
|
||||
]
|
||||
function isStyle(value) {
|
||||
let [cleanedValue] = value.split('?')
|
||||
return styleExtensions.some(extension => cleanedValue?.endsWith(extension))
|
||||
}
|
||||
export { sort_imports_default as default }
|
||||
Generated
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
import { ComparatorByOptionsComputer } from '../../utils/compare/default-comparator-by-options-computer.js'
|
||||
import { SortImportsSortingNode, Options } from './types.js'
|
||||
export declare let comparatorByOptionsComputer: ComparatorByOptionsComputer<
|
||||
Required<Options[number]>,
|
||||
SortImportsSortingNode
|
||||
>
|
||||
Generated
Vendored
+90
@@ -0,0 +1,90 @@
|
||||
import { UnreachableCaseError } from '../../utils/unreachable-case-error.js'
|
||||
import { computeOrderedValue } from '../../utils/compare/compute-ordered-value.js'
|
||||
import { buildLineLengthComparator } from '../../utils/compare/build-line-length-comparator.js'
|
||||
import { compareAlphabetically } from '../../utils/compare/compare-alphabetically.js'
|
||||
import { compareByCustomSort } from '../../utils/compare/compare-by-custom-sort.js'
|
||||
import { unsortedComparator } from '../../utils/compare/unsorted-comparator.js'
|
||||
import { compareNaturally } from '../../utils/compare/compare-naturally.js'
|
||||
import { defaultComparatorByOptionsComputer } from '../../utils/compare/default-comparator-by-options-computer.js'
|
||||
var comparatorByOptionsComputer = options => {
|
||||
switch (options.type) {
|
||||
case 'type-import-first':
|
||||
return (a, b) => compareTypeImportFirst(a, b, options)
|
||||
case 'subgroup-order':
|
||||
case 'alphabetical':
|
||||
case 'line-length':
|
||||
case 'unsorted':
|
||||
case 'natural':
|
||||
case 'custom':
|
||||
switch (options.sortBy) {
|
||||
case 'specifier':
|
||||
return bySpecifierComparatorByOptionsComputer({
|
||||
...options,
|
||||
type: options.type,
|
||||
})
|
||||
case 'path':
|
||||
return defaultComparatorByOptionsComputer({
|
||||
...options,
|
||||
type: options.type,
|
||||
})
|
||||
/* v8 ignore next 2 -- @preserve Exhaustive guard. */
|
||||
default:
|
||||
throw new UnreachableCaseError(options.sortBy)
|
||||
}
|
||||
/* v8 ignore next 2 -- @preserve Exhaustive guard. */
|
||||
default:
|
||||
throw new UnreachableCaseError(options.type)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Compares two import nodes to sort type imports before regular imports.
|
||||
*
|
||||
* When both nodes are type imports or both are regular imports, returns 0
|
||||
* (equal). Otherwise, sorts type imports first based on the order option.
|
||||
*
|
||||
* @param a - The first import sorting node.
|
||||
* @param b - The second import sorting node.
|
||||
* @param options - Options containing the sort order.
|
||||
* @returns A negative number if a should come first, positive if b should.
|
||||
*/
|
||||
function compareTypeImportFirst(a, b, options) {
|
||||
if (a.isTypeImport && b.isTypeImport) {
|
||||
return 0
|
||||
}
|
||||
if (!a.isTypeImport && !b.isTypeImport) {
|
||||
return 0
|
||||
}
|
||||
return computeOrderedValue(a.isTypeImport ? -1 : 1, options.order)
|
||||
}
|
||||
var bySpecifierComparatorByOptionsComputer = options => {
|
||||
switch (options.type) {
|
||||
/* v8 ignore next 2 -- @preserve Untested for now as not a relevant sort for this rule. */
|
||||
case 'subgroup-order':
|
||||
return defaultComparatorByOptionsComputer(options)
|
||||
case 'alphabetical':
|
||||
return (a, b) =>
|
||||
compareAlphabetically(
|
||||
a.specifierName ?? '',
|
||||
b.specifierName ?? '',
|
||||
options,
|
||||
)
|
||||
case 'line-length':
|
||||
return buildLineLengthComparator(options)
|
||||
case 'unsorted':
|
||||
return unsortedComparator
|
||||
case 'natural':
|
||||
return (a, b) =>
|
||||
compareNaturally(a.specifierName ?? '', b.specifierName ?? '', options)
|
||||
case 'custom':
|
||||
return (a, b) =>
|
||||
compareByCustomSort(
|
||||
a.specifierName ?? '',
|
||||
b.specifierName ?? '',
|
||||
options,
|
||||
)
|
||||
/* v8 ignore next 2 -- @preserve Exhaustive guard. */
|
||||
default:
|
||||
throw new UnreachableCaseError(options.type)
|
||||
}
|
||||
}
|
||||
export { comparatorByOptionsComputer }
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user