From f35deef4d2802391bcf3fbd9523df0d8415485dc Mon Sep 17 00:00:00 2001 From: "Azat S." Date: Tue, 12 Sep 2023 13:15:12 +0300 Subject: [PATCH] fix: allow internal imports starting with a hash character --- rules/sort-imports.ts | 13 +- rules/sort-interfaces.ts | 4 +- test/sort-imports.test.ts | 320 +++++++++++++++++++++++++++++++++- utils/is-partition-comment.ts | 10 +- utils/use-groups.ts | 13 +- 5 files changed, 347 insertions(+), 13 deletions(-) diff --git a/rules/sort-imports.ts b/rules/sort-imports.ts index a3aa0c2..c9f1dfa 100644 --- a/rules/sort-imports.ts +++ b/rules/sort-imports.ts @@ -161,8 +161,6 @@ export default createEslintRule, MESSAGE_ID>({ groups: [], }) - let tsPaths: string[] = [] - let source = context.getSourceCode() let nodes: SortingNode[] = [] @@ -194,11 +192,12 @@ export default createEslintRule, MESSAGE_ID>({ let { getGroup, defineGroup, setCustomGroups } = useGroups(options.groups) let isInternal = (nodeElement: TSESTree.ImportDeclaration) => - (options['internal-pattern'].length && - options['internal-pattern'].some(pattern => - minimatch(nodeElement.source.value, pattern), - )) || - tsPaths.some(pattern => minimatch(nodeElement.source.value, pattern)) + options['internal-pattern'].length && + options['internal-pattern'].some(pattern => + minimatch(nodeElement.source.value, pattern, { + nocomment: true, + }), + ) let isCoreModule = (value: string) => builtinModules.includes( diff --git a/rules/sort-interfaces.ts b/rules/sort-interfaces.ts index 8ad0e89..6198f2c 100644 --- a/rules/sort-interfaces.ts +++ b/rules/sort-interfaces.ts @@ -104,7 +104,9 @@ export default createEslintRule, MESSAGE_ID>({ if ( !options['ignore-pattern'].some(pattern => - minimatch(node.id.name, pattern), + minimatch(node.id.name, pattern, { + nocomment: true, + }), ) ) { let source = context.getSourceCode() diff --git a/test/sort-imports.test.ts b/test/sort-imports.test.ts index ef58770..1e9c6d0 100644 --- a/test/sort-imports.test.ts +++ b/test/sort-imports.test.ts @@ -1047,6 +1047,112 @@ describe(RULE_NAME, () => { ], }, ) + + ruleTester.run( + `${RULE_NAME}(${type}): allows to hash symbol in internal pattern`, + rule, + { + valid: [ + { + code: dedent` + import type { Characters } from 'skip-and-loafer' + + import { events } from 'skip-and-loafer' + + import type { Student } from '#school' + + import Satonosuke from '#pets' + import { MitsumiIwakura, SousukeShima } from '#school' + + import { Nao } from '../family' + `, + options: [ + { + ...options, + 'newlines-between': NewlinesBetweenValue.always, + 'internal-pattern': ['#**'], + groups: [ + 'type', + ['builtin', 'external'], + 'internal-type', + 'internal', + ['parent-type', 'sibling-type', 'index-type'], + ['parent', 'sibling', 'index'], + 'object', + 'unknown', + ], + }, + ], + }, + ], + invalid: [ + { + code: dedent` + import type { Student } from '#school' + import type { Characters } from 'skip-and-loafer' + + import Satonosuke from '#pets' + import { events } from 'skip-and-loafer' + import { MitsumiIwakura, SousukeShima } from '#school' + + import { Nao } from '../family' + `, + output: dedent` + import type { Characters } from 'skip-and-loafer' + + import { events } from 'skip-and-loafer' + + import type { Student } from '#school' + + import Satonosuke from '#pets' + import { MitsumiIwakura, SousukeShima } from '#school' + + import { Nao } from '../family' + `, + options: [ + { + ...options, + 'newlines-between': NewlinesBetweenValue.always, + 'internal-pattern': ['#**'], + groups: [ + 'type', + ['builtin', 'external'], + 'internal-type', + 'internal', + ['parent-type', 'sibling-type', 'index-type'], + ['parent', 'sibling', 'index'], + 'object', + 'unknown', + ], + }, + ], + errors: [ + { + messageId: 'unexpectedImportsOrder', + data: { + left: '#school', + right: 'skip-and-loafer', + }, + }, + { + messageId: 'unexpectedImportsOrder', + data: { + left: '#pets', + right: 'skip-and-loafer', + }, + }, + { + messageId: 'missedSpacingBetweenImports', + data: { + left: 'skip-and-loafer', + right: '#school', + }, + }, + ], + }, + ], + }, + ) }) describe(`${RULE_NAME}: sorting by natural order`, () => { @@ -2079,6 +2185,112 @@ describe(RULE_NAME, () => { ], }, ) + + ruleTester.run( + `${RULE_NAME}(${type}): allows to hash symbol in internal pattern`, + rule, + { + valid: [ + { + code: dedent` + import type { Characters } from 'skip-and-loafer' + + import { events } from 'skip-and-loafer' + + import type { Student } from '#school' + + import Satonosuke from '#pets' + import { MitsumiIwakura, SousukeShima } from '#school' + + import { Nao } from '../family' + `, + options: [ + { + ...options, + 'newlines-between': NewlinesBetweenValue.always, + 'internal-pattern': ['#**'], + groups: [ + 'type', + ['builtin', 'external'], + 'internal-type', + 'internal', + ['parent-type', 'sibling-type', 'index-type'], + ['parent', 'sibling', 'index'], + 'object', + 'unknown', + ], + }, + ], + }, + ], + invalid: [ + { + code: dedent` + import type { Student } from '#school' + import type { Characters } from 'skip-and-loafer' + + import Satonosuke from '#pets' + import { events } from 'skip-and-loafer' + import { MitsumiIwakura, SousukeShima } from '#school' + + import { Nao } from '../family' + `, + output: dedent` + import type { Characters } from 'skip-and-loafer' + + import { events } from 'skip-and-loafer' + + import type { Student } from '#school' + + import Satonosuke from '#pets' + import { MitsumiIwakura, SousukeShima } from '#school' + + import { Nao } from '../family' + `, + options: [ + { + ...options, + 'newlines-between': NewlinesBetweenValue.always, + 'internal-pattern': ['#**'], + groups: [ + 'type', + ['builtin', 'external'], + 'internal-type', + 'internal', + ['parent-type', 'sibling-type', 'index-type'], + ['parent', 'sibling', 'index'], + 'object', + 'unknown', + ], + }, + ], + errors: [ + { + messageId: 'unexpectedImportsOrder', + data: { + left: '#school', + right: 'skip-and-loafer', + }, + }, + { + messageId: 'unexpectedImportsOrder', + data: { + left: '#pets', + right: 'skip-and-loafer', + }, + }, + { + messageId: 'missedSpacingBetweenImports', + data: { + left: 'skip-and-loafer', + right: '#school', + }, + }, + ], + }, + ], + }, + ) }) describe(`${RULE_NAME}: sorting by line length`, () => { @@ -3159,6 +3371,112 @@ describe(RULE_NAME, () => { ], }, ) + + ruleTester.run( + `${RULE_NAME}(${type}): allows to hash symbol in internal pattern`, + rule, + { + valid: [ + { + code: dedent` + import type { Characters } from 'skip-and-loafer' + + import { events } from 'skip-and-loafer' + + import type { Student } from '#school' + + import { MitsumiIwakura, SousukeShima } from '#school' + import Satonosuke from '#pets' + + import { Nao } from '../family' + `, + options: [ + { + ...options, + 'newlines-between': NewlinesBetweenValue.always, + 'internal-pattern': ['#**'], + groups: [ + 'type', + ['builtin', 'external'], + 'internal-type', + 'internal', + ['parent-type', 'sibling-type', 'index-type'], + ['parent', 'sibling', 'index'], + 'object', + 'unknown', + ], + }, + ], + }, + ], + invalid: [ + { + code: dedent` + import type { Student } from '#school' + import type { Characters } from 'skip-and-loafer' + + import Satonosuke from '#pets' + import { events } from 'skip-and-loafer' + import { MitsumiIwakura, SousukeShima } from '#school' + + import { Nao } from '../family' + `, + output: dedent` + import type { Characters } from 'skip-and-loafer' + + import { events } from 'skip-and-loafer' + + import type { Student } from '#school' + + import { MitsumiIwakura, SousukeShima } from '#school' + import Satonosuke from '#pets' + + import { Nao } from '../family' + `, + options: [ + { + ...options, + 'newlines-between': NewlinesBetweenValue.always, + 'internal-pattern': ['#**'], + groups: [ + 'type', + ['builtin', 'external'], + 'internal-type', + 'internal', + ['parent-type', 'sibling-type', 'index-type'], + ['parent', 'sibling', 'index'], + 'object', + 'unknown', + ], + }, + ], + errors: [ + { + messageId: 'unexpectedImportsOrder', + data: { + left: '#school', + right: 'skip-and-loafer', + }, + }, + { + messageId: 'unexpectedImportsOrder', + data: { + left: '#pets', + right: 'skip-and-loafer', + }, + }, + { + messageId: 'missedSpacingBetweenImports', + data: { + left: 'skip-and-loafer', + right: '#school', + }, + }, + ], + }, + ], + }, + ) }) describe(`${RULE_NAME}: misc`, () => { @@ -3241,7 +3559,7 @@ describe(RULE_NAME, () => { { code: dedent` import { writeFile } from 'node:fs/promises' - + import { useEffect } from 'react' `, options: [ diff --git a/utils/is-partition-comment.ts b/utils/is-partition-comment.ts index 887387d..00fe30d 100644 --- a/utils/is-partition-comment.ts +++ b/utils/is-partition-comment.ts @@ -7,7 +7,13 @@ export let isPartitionComment = ( comment: string, ) => (Array.isArray(partitionComment) && - partitionComment.some(pattern => minimatch(comment.trim(), pattern))) || + partitionComment.some(pattern => + minimatch(comment.trim(), pattern, { + nocomment: true, + }), + )) || (typeof partitionComment === 'string' && - minimatch(comment.trim(), partitionComment)) || + minimatch(comment.trim(), partitionComment, { + nocomment: true, + })) || partitionComment === true diff --git a/utils/use-groups.ts b/utils/use-groups.ts index cc74d13..fcf0cc1 100644 --- a/utils/use-groups.ts +++ b/utils/use-groups.ts @@ -21,12 +21,21 @@ export let useGroups = (groups: (string[] | string)[]) => { for (let [key, pattern] of Object.entries(customGroups)) { if ( Array.isArray(pattern) && - pattern.some(patternValue => minimatch(name, patternValue)) + pattern.some(patternValue => + minimatch(name, patternValue, { + nocomment: true, + }), + ) ) { defineGroup(key) } - if (typeof pattern === 'string' && minimatch(name, pattern)) { + if ( + typeof pattern === 'string' && + minimatch(name, pattern, { + nocomment: true, + }) + ) { defineGroup(key) } }