diff --git a/rules/sort-astro-attributes.ts b/rules/sort-astro-attributes.ts index f69f770d..cece4221 100644 --- a/rules/sort-astro-attributes.ts +++ b/rules/sort-astro-attributes.ts @@ -1,14 +1,15 @@ import type { TSESTree } from '@typescript-eslint/types' import type { AST } from 'astro-eslint-parser' -import { minimatch } from 'minimatch' import path from 'path' import type { SortingNode } from '../typings' import { createEslintRule } from '../utils/create-eslint-rule' +import { getGroupNumber } from '../utils/get-group-number' import { rangeToDiff } from '../utils/range-to-diff' import { SortOrder, SortType } from '../typings' +import { useGroups } from '../utils/use-groups' import { makeFixes } from '../utils/make-fixes' import { sortNodes } from '../utils/sort-nodes' import { pairwise } from '../utils/pairwise' @@ -22,10 +23,6 @@ type Group = | 'unknown' | T[number] -type SortingNodeWithGroup = SortingNode & { - group: Group -} - type MESSAGE_ID = 'unexpectedAstroAttributesOrder' type Options = [ @@ -112,8 +109,8 @@ export default createEslintRule, MESSAGE_ID>({ let source = context.getSourceCode() - let parts: SortingNodeWithGroup[][] = attributes.reduce( - (accumulator: SortingNodeWithGroup[][], attribute) => { + let parts: SortingNode[][] = attributes.reduce( + (accumulator: SortingNode[][], attribute) => { if (attribute.type === 'JSXSpreadAttribute') { accumulator.push([]) return accumulator @@ -124,28 +121,11 @@ export default createEslintRule, MESSAGE_ID>({ ? attribute.name.name : source.text.slice(...attribute.name.range) - let group: Group | undefined + let { getGroup, defineGroup, setCustomGroups } = useGroups( + options.groups, + ) - let defineGroup = (nodeGroup: Group) => { - if (!group && options.groups.flat().includes(nodeGroup)) { - group = nodeGroup - } - } - - for (let [key, pattern] of Object.entries( - options['custom-groups'], - )) { - if ( - Array.isArray(pattern) && - pattern.some(patternValue => minimatch(name, patternValue)) - ) { - defineGroup(key) - } - - if (typeof pattern === 'string' && minimatch(name, pattern)) { - defineGroup(key) - } - } + setCustomGroups(options['custom-groups'], name) if (attribute.type === 'AstroShorthandAttribute') { defineGroup('astro-shorthand') @@ -163,7 +143,7 @@ export default createEslintRule, MESSAGE_ID>({ accumulator.at(-1)!.push({ size: rangeToDiff(attribute.range), node: attribute as unknown as TSESTree.Node, - group: group ?? 'unknown', + group: getGroup(), name, }) @@ -172,27 +152,10 @@ export default createEslintRule, MESSAGE_ID>({ [[]], ) - let getGroupNumber = ( - nodeWithGroup: SortingNodeWithGroup, - ): number => { - for (let i = 0, max = options.groups.length; i < max; i++) { - let currentGroup = options.groups[i] - - if ( - nodeWithGroup.group === currentGroup || - (Array.isArray(currentGroup) && - currentGroup.includes(nodeWithGroup.group)) - ) { - return i - } - } - return options.groups.length - } - for (let nodes of parts) { pairwise(nodes, (left, right) => { - let leftNum = getGroupNumber(left) - let rightNum = getGroupNumber(right) + let leftNum = getGroupNumber(options.groups, left) + let rightNum = getGroupNumber(options.groups, right) if ( leftNum > rightNum || @@ -207,11 +170,11 @@ export default createEslintRule, MESSAGE_ID>({ node: right.node, fix: fixer => { let grouped: { - [key: string]: SortingNodeWithGroup[] + [key: string]: SortingNode[] } = {} for (let currentNode of nodes) { - let groupNum = getGroupNumber(currentNode) + let groupNum = getGroupNumber(options.groups, currentNode) if (!(groupNum in grouped)) { grouped[groupNum] = [currentNode] diff --git a/rules/sort-classes.ts b/rules/sort-classes.ts index 598e5559..d9f5947f 100644 --- a/rules/sort-classes.ts +++ b/rules/sort-classes.ts @@ -5,10 +5,12 @@ import { AST_NODE_TYPES } from '@typescript-eslint/types' import type { SortingNode } from '../typings' import { createEslintRule } from '../utils/create-eslint-rule' +import { getGroupNumber } from '../utils/get-group-number' import { toSingleLine } from '../utils/to-single-line' import { getNodeRange } from '../utils/get-node-range' import { rangeToDiff } from '../utils/range-to-diff' import { SortOrder, SortType } from '../typings' +import { useGroups } from '../utils/use-groups' import { sortNodes } from '../utils/sort-nodes' import { complete } from '../utils/complete' import { pairwise } from '../utils/pairwise' @@ -35,8 +37,6 @@ type Options = [ }>, ] -type SortingNodeWithGroup = SortingNode & { group: Group } - export const RULE_NAME = 'sort-classes' export default createEslintRule({ @@ -98,9 +98,9 @@ export default createEslintRule({ let source = context.getSourceCode() - let nodes: SortingNodeWithGroup[] = node.body.map(member => { - let group: undefined | Group + let nodes: SortingNode[] = node.body.map(member => { let name: string + let { getGroup, defineGroup } = useGroups(options.groups) if (member.type === AST_NODE_TYPES.StaticBlock) { name = 'static' @@ -117,12 +117,6 @@ export default createEslintRule({ } } - let defineGroup = (nodeGroup: Group) => { - if (!group && options.groups.flat().includes(nodeGroup)) { - group = nodeGroup - } - } - if (member.type === AST_NODE_TYPES.MethodDefinition) { if (member.kind === 'constructor') { defineGroup('constructor') @@ -151,30 +145,15 @@ export default createEslintRule({ return { size: rangeToDiff(member.range), - group: group ?? 'unknown', + group: getGroup(), node: member, name, } }) - let getGroupNumber = (sortingNode: SortingNodeWithGroup): number => { - for (let i = 0, max = options.groups.length; i < max; i++) { - let currentGroup = options.groups[i] - - if ( - sortingNode.group === currentGroup || - (Array.isArray(currentGroup) && - currentGroup.includes(sortingNode.group)) - ) { - return i - } - } - return options.groups.length - } - pairwise(nodes, (left, right) => { - let leftNum = getGroupNumber(left) - let rightNum = getGroupNumber(right) + let leftNum = getGroupNumber(options.groups, left) + let rightNum = getGroupNumber(options.groups, right) if ( leftNum > rightNum || @@ -193,11 +172,11 @@ export default createEslintRule({ let grouped = nodes.reduce( ( accumulator: { - [key: string]: SortingNodeWithGroup[] + [key: string]: SortingNode[] }, sortingNode, ) => { - let groupNum = getGroupNumber(sortingNode) + let groupNum = getGroupNumber(options.groups, sortingNode) if (!(groupNum in accumulator)) { accumulator[groupNum] = [sortingNode] @@ -216,7 +195,7 @@ export default createEslintRule({ let formatted = Object.keys(grouped) .sort() .reduce( - (accumulator: SortingNodeWithGroup[], group: string) => [ + (accumulator: SortingNode[], group: string) => [ ...accumulator, ...grouped[group], ], diff --git a/rules/sort-imports.ts b/rules/sort-imports.ts index 15c068d1..75fa397e 100644 --- a/rules/sort-imports.ts +++ b/rules/sort-imports.ts @@ -9,9 +9,11 @@ import type { SortingNode } from '../typings' import { getCommentBefore } from '../utils/get-comment-before' import { createEslintRule } from '../utils/create-eslint-rule' +import { getGroupNumber } from '../utils/get-group-number' import { getNodeRange } from '../utils/get-node-range' import { rangeToDiff } from '../utils/range-to-diff' import { SortOrder, SortType } from '../typings' +import { useGroups } from '../utils/use-groups' import { sortNodes } from '../utils/sort-nodes' import { complete } from '../utils/complete' import { pairwise } from '../utils/pairwise' @@ -69,10 +71,6 @@ type ModuleDeclaration = | TSESTree.TSImportEqualsDeclaration | TSESTree.ImportDeclaration -type SortingNodeWithGroup = SortingNode & { - group: Group -} - export default createEslintRule, MESSAGE_ID>({ name: RULE_NAME, meta: { @@ -163,11 +161,9 @@ export default createEslintRule, MESSAGE_ID>({ let source = context.getSourceCode() - let nodes: SortingNodeWithGroup[] = [] + let nodes: SortingNode[] = [] let computeGroup = (node: ModuleDeclaration): Group => { - let group: Group | undefined - let isStyle = (value: string) => ['.less', '.scss', '.sass', '.styl', '.pcss', '.css', '.sss'].some( extension => value.endsWith(extension), @@ -188,11 +184,7 @@ export default createEslintRule, MESSAGE_ID>({ let isSibling = (value: string) => value.indexOf('./') === 0 - let defineGroup = (nodeGroup: Group) => { - if (!group && options.groups.flat().includes(nodeGroup)) { - group = nodeGroup - } - } + let { getGroup, defineGroup, setCustomGroups } = useGroups(options.groups) let isInternal = (nodeElement: TSESTree.ImportDeclaration) => (options['internal-pattern'].length && @@ -201,29 +193,9 @@ export default createEslintRule, MESSAGE_ID>({ )) || tsPaths.some(pattern => minimatch(nodeElement.source.value, pattern)) - let determineCustomGroup = ( - groupType: 'value' | 'type', - value: string, - ) => { - for (let [key, pattern] of Object.entries( - options['custom-groups'][groupType] ?? {}, - )) { - if ( - Array.isArray(pattern) && - pattern.some(patternValue => minimatch(value, patternValue)) - ) { - defineGroup(key) - } - - if (typeof pattern === 'string' && minimatch(value, pattern)) { - defineGroup(key) - } - } - } - if (node.importKind === 'type') { if (node.type === AST_NODE_TYPES.ImportDeclaration) { - determineCustomGroup('type', node.source.value) + setCustomGroups(options['custom-groups'].type, node.source.value) if (isCoreModule(node.source.value)) { defineGroup('builtin-type') @@ -250,8 +222,8 @@ export default createEslintRule, MESSAGE_ID>({ defineGroup('type') } - if (!group && node.type === AST_NODE_TYPES.ImportDeclaration) { - determineCustomGroup('value', node.source.value) + if (node.type === AST_NODE_TYPES.ImportDeclaration) { + setCustomGroups(options['custom-groups'].value, node.source.value) if (isCoreModule(node.source.value)) { defineGroup('builtin') @@ -284,7 +256,7 @@ export default createEslintRule, MESSAGE_ID>({ defineGroup('external') } - return group ?? 'unknown' + return getGroup() } let registerNode = (node: ModuleDeclaration) => { @@ -316,20 +288,6 @@ export default createEslintRule, MESSAGE_ID>({ TSImportEqualsDeclaration: registerNode, ImportDeclaration: registerNode, 'Program:exit': () => { - let getGroupNumber = (node: SortingNodeWithGroup): number => { - for (let i = 0, max = options.groups.length; i < max; i++) { - let currentGroup = options.groups[i] - - if ( - node.group === currentGroup || - (Array.isArray(currentGroup) && currentGroup.includes(node.group)) - ) { - return i - } - } - return options.groups.length - } - let hasContentBetweenNodes = ( left: SortingNode, right: SortingNode, @@ -356,16 +314,16 @@ export default createEslintRule, MESSAGE_ID>({ let fix = ( fixer: TSESLint.RuleFixer, - nodesToFix: SortingNodeWithGroup[], + nodesToFix: SortingNode[], ): TSESLint.RuleFix[] => { let fixes: TSESLint.RuleFix[] = [] let grouped: { - [key: string]: SortingNodeWithGroup[] + [key: string]: SortingNode[] } = {} for (let node of nodesToFix) { - let groupNum = getGroupNumber(node) + let groupNum = getGroupNumber(options.groups, node) if (!(groupNum in grouped)) { grouped[groupNum] = [node] @@ -380,10 +338,10 @@ export default createEslintRule, MESSAGE_ID>({ let formatted = Object.keys(grouped) .sort() .reduce( - ( - accumulator: SortingNodeWithGroup[], - group: string, - ) => [...accumulator, ...grouped[group]], + (accumulator: SortingNode[], group: string) => [ + ...accumulator, + ...grouped[group], + ], [], ) @@ -408,7 +366,8 @@ export default createEslintRule, MESSAGE_ID>({ if ( (options['newlines-between'] === 'always' && - getGroupNumber(node) === getGroupNumber(nextNode) && + getGroupNumber(options.groups, node) === + getGroupNumber(options.groups, nextNode) && linesBetweenImports !== 0) || (options['newlines-between'] === 'never' && linesBetweenImports > 0) @@ -424,7 +383,8 @@ export default createEslintRule, MESSAGE_ID>({ if ( options['newlines-between'] === 'always' && - getGroupNumber(node) !== getGroupNumber(nextNode) && + getGroupNumber(options.groups, node) !== + getGroupNumber(options.groups, nextNode) && linesBetweenImports > 1 ) { fixes.push( @@ -442,7 +402,8 @@ export default createEslintRule, MESSAGE_ID>({ if ( options['newlines-between'] === 'always' && - getGroupNumber(node) !== getGroupNumber(nextNode) && + getGroupNumber(options.groups, node) !== + getGroupNumber(options.groups, nextNode) && linesBetweenImports === 0 ) { fixes.push( @@ -459,7 +420,7 @@ export default createEslintRule, MESSAGE_ID>({ return fixes } - let splittedNodes: SortingNodeWithGroup[][] = [[]] + let splittedNodes: SortingNode[][] = [[]] for (let node of nodes) { let lastNode = splittedNodes.at(-1)?.at(-1) @@ -473,8 +434,8 @@ export default createEslintRule, MESSAGE_ID>({ for (let nodeList of splittedNodes) { pairwise(nodeList, (left, right) => { - let leftNum = getGroupNumber(left) - let rightNum = getGroupNumber(right) + let leftNum = getGroupNumber(options.groups, left) + let rightNum = getGroupNumber(options.groups, right) let numberOfEmptyLinesBetween = getLinesBetweenImports(left, right) diff --git a/rules/sort-jsx-props.ts b/rules/sort-jsx-props.ts index 603f0a60..15f55b0d 100644 --- a/rules/sort-jsx-props.ts +++ b/rules/sort-jsx-props.ts @@ -1,14 +1,15 @@ import type { TSESTree } from '@typescript-eslint/types' import { AST_NODE_TYPES } from '@typescript-eslint/types' -import { minimatch } from 'minimatch' import type { SortingNode } from '../typings' import { createEslintRule } from '../utils/create-eslint-rule' +import { getGroupNumber } from '../utils/get-group-number' import { rangeToDiff } from '../utils/range-to-diff' import { SortOrder, SortType } from '../typings' import { makeFixes } from '../utils/make-fixes' +import { useGroups } from '../utils/use-groups' import { sortNodes } from '../utils/sort-nodes' import { pairwise } from '../utils/pairwise' import { complete } from '../utils/complete' @@ -22,10 +23,6 @@ type Group = | 'unknown' | T[number] -type SortingNodeWithGroup = SortingNode & { - group: Group -} - type Options = [ Partial<{ 'custom-groups': { [key in T[number]]: string[] | string } @@ -101,88 +98,53 @@ export default createEslintRule, MESSAGE_ID>({ let source = context.getSourceCode() - let parts: SortingNodeWithGroup[][] = - node.openingElement.attributes.reduce( - ( - accumulator: SortingNodeWithGroup[][], - attribute: TSESTree.JSXSpreadAttribute | TSESTree.JSXAttribute, - ) => { - if (attribute.type === AST_NODE_TYPES.JSXSpreadAttribute) { - accumulator.push([]) - return accumulator - } - - let name = - attribute.name.type === AST_NODE_TYPES.JSXNamespacedName - ? `${attribute.name.namespace.name}:${attribute.name.name.name}` - : attribute.name.name - - let group: Group | undefined - - let defineGroup = (nodeGroup: Group) => { - if (!group && options.groups.flat().includes(nodeGroup)) { - group = nodeGroup - } - } - - for (let [key, pattern] of Object.entries( - options['custom-groups'], - )) { - if ( - Array.isArray(pattern) && - pattern.some(patternValue => minimatch(name, patternValue)) - ) { - defineGroup(key) - } - - if (typeof pattern === 'string' && minimatch(name, pattern)) { - defineGroup(key) - } - } - - if (attribute.value === null) { - defineGroup('shorthand') - } - - if (attribute.loc.start.line !== attribute.loc.end.line) { - defineGroup('multiline') - } - - let jsxNode = { - size: rangeToDiff(attribute.range), - group: group ?? 'unknown', - node: attribute, - name, - } - - accumulator.at(-1)!.push(jsxNode) - + let parts: SortingNode[][] = node.openingElement.attributes.reduce( + ( + accumulator: SortingNode[][], + attribute: TSESTree.JSXSpreadAttribute | TSESTree.JSXAttribute, + ) => { + if (attribute.type === AST_NODE_TYPES.JSXSpreadAttribute) { + accumulator.push([]) return accumulator - }, - [[]], - ) + } - let getGroupNumber = ( - nodeWithGroup: SortingNodeWithGroup, - ): number => { - for (let i = 0, max = options.groups.length; i < max; i++) { - let currentGroup = options.groups[i] + let name = + attribute.name.type === AST_NODE_TYPES.JSXNamespacedName + ? `${attribute.name.namespace.name}:${attribute.name.name.name}` + : attribute.name.name - if ( - nodeWithGroup.group === currentGroup || - (Array.isArray(currentGroup) && - currentGroup.includes(nodeWithGroup.group)) - ) { - return i + let { getGroup, defineGroup, setCustomGroups } = useGroups( + options.groups, + ) + + setCustomGroups(options['custom-groups'], name) + + if (attribute.value === null) { + defineGroup('shorthand') } - } - return options.groups.length - } + + if (attribute.loc.start.line !== attribute.loc.end.line) { + defineGroup('multiline') + } + + let jsxNode = { + size: rangeToDiff(attribute.range), + group: getGroup(), + node: attribute, + name, + } + + accumulator.at(-1)!.push(jsxNode) + + return accumulator + }, + [[]], + ) for (let nodes of parts) { pairwise(nodes, (left, right) => { - let leftNum = getGroupNumber(left) - let rightNum = getGroupNumber(right) + let leftNum = getGroupNumber(options.groups, left) + let rightNum = getGroupNumber(options.groups, right) if ( leftNum > rightNum || @@ -197,11 +159,11 @@ export default createEslintRule, MESSAGE_ID>({ node: right.node, fix: fixer => { let grouped: { - [key: string]: SortingNodeWithGroup[] + [key: string]: SortingNode[] } = {} for (let currentNode of nodes) { - let groupNum = getGroupNumber(currentNode) + let groupNum = getGroupNumber(options.groups, currentNode) if (!(groupNum in grouped)) { grouped[groupNum] = [currentNode] diff --git a/rules/sort-svelte-attributes.ts b/rules/sort-svelte-attributes.ts index 3cac5164..510273ca 100644 --- a/rules/sort-svelte-attributes.ts +++ b/rules/sort-svelte-attributes.ts @@ -1,14 +1,15 @@ import type { TSESTree } from '@typescript-eslint/types' import type { AST } from 'svelte-eslint-parser' -import { minimatch } from 'minimatch' import path from 'path' import type { SortingNode } from '../typings' import { createEslintRule } from '../utils/create-eslint-rule' +import { getGroupNumber } from '../utils/get-group-number' import { rangeToDiff } from '../utils/range-to-diff' import { SortOrder, SortType } from '../typings' +import { useGroups } from '../utils/use-groups' import { sortNodes } from '../utils/sort-nodes' import { makeFixes } from '../utils/make-fixes' import { complete } from '../utils/complete' @@ -24,10 +25,6 @@ type Group = | 'unknown' | T[number] -type SortingNodeWithGroup = SortingNode & { - group: Group -} - type Options = [ Partial<{ 'custom-groups': { [key in T[number]]: string[] | string } @@ -109,98 +106,63 @@ export default createEslintRule, MESSAGE_ID>({ let source = context.getSourceCode() - let parts: SortingNodeWithGroup[][] = - node.attributes.reduce( - (accumulator: SortingNodeWithGroup[][], attribute) => { - if (attribute.type === 'SvelteSpreadAttribute') { - accumulator.push([]) - return accumulator - } - - let name: string + let parts: SortingNode[][] = node.attributes.reduce( + (accumulator: SortingNode[][], attribute) => { + if (attribute.type === 'SvelteSpreadAttribute') { + accumulator.push([]) + return accumulator + } - let group: Group | undefined + let name: string - let defineGroup = (nodeGroup: Group) => { - if (!group && options.groups.flat().includes(nodeGroup)) { - group = nodeGroup - } - } + let { getGroup, defineGroup, setCustomGroups } = useGroups( + options.groups, + ) - if (attribute.key.type === 'SvelteSpecialDirectiveKey') { - name = source.text.slice(...attribute.key.range) + if (attribute.key.type === 'SvelteSpecialDirectiveKey') { + name = source.text.slice(...attribute.key.range) + } else { + if (typeof attribute.key.name === 'string') { + ;({ name } = attribute.key) } else { - if (typeof attribute.key.name === 'string') { - ;({ name } = attribute.key) - } else { - name = source.text.slice(...attribute.key.range!) - } - } - - for (let [key, pattern] of Object.entries( - options['custom-groups'], - )) { - if ( - Array.isArray(pattern) && - pattern.some(patternValue => minimatch(name, patternValue)) - ) { - defineGroup(key) - } - - if (typeof pattern === 'string' && minimatch(name, pattern)) { - defineGroup(key) - } - } - - if (attribute.type === 'SvelteShorthandAttribute') { - defineGroup('svelte-shorthand') - defineGroup('shorthand') - } - - if ( - !('value' in attribute) || - (Array.isArray(attribute.value) && !attribute.value.at(0)) - ) { - defineGroup('shorthand') + name = source.text.slice(...attribute.key.range!) } + } - if (attribute.loc.start.line !== attribute.loc.end.line) { - defineGroup('multiline') - } - - accumulator.at(-1)!.push({ - size: rangeToDiff(attribute.range), - node: attribute as unknown as TSESTree.Node, - group: group ?? 'unknown', - name, - }) - - return accumulator - }, - [[]], - ) + setCustomGroups(options['custom-groups'], name) - let getGroupNumber = ( - nodeWithGroup: SortingNodeWithGroup, - ): number => { - for (let i = 0, max = options.groups.length; i < max; i++) { - let currentGroup = options.groups[i] + if (attribute.type === 'SvelteShorthandAttribute') { + defineGroup('svelte-shorthand') + defineGroup('shorthand') + } if ( - nodeWithGroup.group === currentGroup || - (Array.isArray(currentGroup) && - currentGroup.includes(nodeWithGroup.group)) + !('value' in attribute) || + (Array.isArray(attribute.value) && !attribute.value.at(0)) ) { - return i + defineGroup('shorthand') } - } - return options.groups.length - } + + if (attribute.loc.start.line !== attribute.loc.end.line) { + defineGroup('multiline') + } + + accumulator.at(-1)!.push({ + size: rangeToDiff(attribute.range), + node: attribute as unknown as TSESTree.Node, + group: getGroup(), + name, + }) + + return accumulator + }, + [[]], + ) for (let nodes of parts) { pairwise(nodes, (left, right) => { - let leftNum = getGroupNumber(left) - let rightNum = getGroupNumber(right) + let leftNum = getGroupNumber(options.groups, left) + let rightNum = getGroupNumber(options.groups, right) if ( leftNum > rightNum || @@ -215,11 +177,11 @@ export default createEslintRule, MESSAGE_ID>({ node: right.node, fix: fixer => { let grouped: { - [key: string]: SortingNodeWithGroup[] + [key: string]: SortingNode[] } = {} for (let currentNode of nodes) { - let groupNum = getGroupNumber(currentNode) + let groupNum = getGroupNumber(options.groups, currentNode) if (!(groupNum in grouped)) { grouped[groupNum] = [currentNode] diff --git a/typings/index.ts b/typings/index.ts index 9ee13914..aea0eb6f 100644 --- a/typings/index.ts +++ b/typings/index.ts @@ -16,6 +16,7 @@ export type PartitionComment = string[] | boolean | string export interface SortingNode { dependencies?: string[] node: TSESTree.Node + group?: string name: string size: number } diff --git a/utils/get-group-number.ts b/utils/get-group-number.ts new file mode 100644 index 00000000..eccf628b --- /dev/null +++ b/utils/get-group-number.ts @@ -0,0 +1,21 @@ +import type { SortingNode } from '../typings' + +export let getGroupNumber = ( + groups: (string[] | string)[], + node: SortingNode, +): number => { + for (let i = 0, max = groups.length; i < max; i++) { + let currentGroup = groups[i] + + if ( + node.group === currentGroup || + (Array.isArray(currentGroup) && + typeof node.group === 'string' && + currentGroup.includes(node.group)) + ) { + return i + } + } + + return groups.length +} diff --git a/utils/group-by.ts b/utils/group-by.ts index daa9c562..f847d9f7 100644 --- a/utils/group-by.ts +++ b/utils/group-by.ts @@ -1,11 +1,14 @@ export let groupBy = (array: T[], predicate: (v: T) => string) => - array.reduce((acc, value) => { - let computedValue = predicate(value) + array.reduce( + (acc, value) => { + let computedValue = predicate(value) - if (!(computedValue in acc)) { - acc[computedValue] = [] - } + if (!(computedValue in acc)) { + acc[computedValue] = [] + } - acc[computedValue].push(value) - return acc - }, {} as { [key: string]: T[] }) + acc[computedValue].push(value) + return acc + }, + {} as { [key: string]: T[] }, + ) diff --git a/utils/use-groups.ts b/utils/use-groups.ts new file mode 100644 index 00000000..cc74d13c --- /dev/null +++ b/utils/use-groups.ts @@ -0,0 +1,41 @@ +import { minimatch } from 'minimatch' + +export let useGroups = (groups: (string[] | string)[]) => { + let group: undefined | string + + let defineGroup = (value: string) => { + if (!group && groups.flat().includes(value)) { + group = value + } + } + + let setCustomGroups = ( + customGroups: + | { + [key: string]: string[] | string + } + | undefined, + name: string, + ) => { + if (customGroups) { + for (let [key, pattern] of Object.entries(customGroups)) { + if ( + Array.isArray(pattern) && + pattern.some(patternValue => minimatch(name, patternValue)) + ) { + defineGroup(key) + } + + if (typeof pattern === 'string' && minimatch(name, pattern)) { + defineGroup(key) + } + } + } + } + + return { + getGroup: () => group ?? 'unknown', + setCustomGroups, + defineGroup, + } +}