Skip to content

Commit 0ff5e45

Browse files
authoredAug 1, 2023
feat: format for named tuple (#229)
1 parent effc708 commit 0ff5e45

File tree

4 files changed

+177
-0
lines changed

4 files changed

+177
-0
lines changed
 

‎packages/eslint-config-ts/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ module.exports = {
161161
'antfu/no-cjs-exports': 'error',
162162
'antfu/no-ts-export-equal': 'error',
163163
'antfu/no-const-enum': 'error',
164+
'antfu/named-tuple-spacing': 'error',
164165

165166
// off
166167
'@typescript-eslint/consistent-indexed-object-style': 'off',

‎packages/eslint-plugin-antfu/src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import topLevelFunction from './rules/top-level-function'
66
import noTsExportEqual from './rules/no-ts-export-equal'
77
import noCjsExports from './rules/no-cjs-exports'
88
import noConstEnum from './rules/no-const-enum'
9+
import namedTupleSpacing from './rules/named-tuple-spacing'
910

1011
export default {
1112
rules: {
@@ -17,5 +18,6 @@ export default {
1718
'no-cjs-exports': noCjsExports,
1819
'no-ts-export-equal': noTsExportEqual,
1920
'no-const-enum': noConstEnum,
21+
'named-tuple-spacing': namedTupleSpacing,
2022
},
2123
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { RuleTester } from '@typescript-eslint/utils/dist/ts-eslint'
2+
import { it } from 'vitest'
3+
import rule, { RULE_NAME } from './named-tuple-spacing'
4+
5+
const valids = [
6+
'type T = [i: number]',
7+
'type T = [i?: number]',
8+
'type T = [i: number, j: number]',
9+
`const emit = defineEmits<{
10+
change: [id: number]
11+
update: [value: string]
12+
}>()`,
13+
]
14+
15+
it('runs', () => {
16+
const ruleTester: RuleTester = new RuleTester({
17+
parser: require.resolve('@typescript-eslint/parser'),
18+
})
19+
20+
ruleTester.run(RULE_NAME, rule, {
21+
valid: valids,
22+
invalid: [
23+
{
24+
code: 'type T = [i:number]',
25+
output: 'type T = [i: number]',
26+
errors: [{ messageId: 'expectedSpaceAfter' }],
27+
},
28+
{
29+
code: 'type T = [i: number]',
30+
output: 'type T = [i: number]',
31+
errors: [{ messageId: 'expectedSpaceAfter' }],
32+
},
33+
{
34+
code: 'type T = [i?:number]',
35+
output: 'type T = [i?: number]',
36+
errors: [{ messageId: 'expectedSpaceAfter' }],
37+
},
38+
{
39+
code: 'type T = [i? :number]',
40+
output: 'type T = [i?: number]',
41+
errors: [{ messageId: 'unexpectedSpaceBetween' }, { messageId: 'expectedSpaceAfter' }],
42+
},
43+
{
44+
code: 'type T = [i : number]',
45+
output: 'type T = [i: number]',
46+
errors: [{ messageId: 'unexpectedSpaceBefore' }],
47+
},
48+
{
49+
code: 'type T = [i : number]',
50+
output: 'type T = [i: number]',
51+
errors: [{ messageId: 'unexpectedSpaceBefore' }],
52+
},
53+
{
54+
code: 'type T = [i ? : number]',
55+
output: 'type T = [i?: number]',
56+
errors: [{ messageId: 'unexpectedSpaceBetween' }, { messageId: 'unexpectedSpaceBefore' }],
57+
},
58+
{
59+
code: 'type T = [i:number, j:number]',
60+
output: 'type T = [i: number, j: number]',
61+
errors: [{ messageId: 'expectedSpaceAfter' }, { messageId: 'expectedSpaceAfter' }],
62+
},
63+
{
64+
code: `
65+
const emit = defineEmits<{
66+
change: [id:number]
67+
update: [value:string]
68+
}>()
69+
`,
70+
output: `
71+
const emit = defineEmits<{
72+
change: [id: number]
73+
update: [value: string]
74+
}>()
75+
`,
76+
errors: [{ messageId: 'expectedSpaceAfter' }, { messageId: 'expectedSpaceAfter' }],
77+
},
78+
{
79+
code: `
80+
const emit = defineEmits<{
81+
change: [id? :number]
82+
update: [value:string]
83+
}>()
84+
`,
85+
output: `
86+
const emit = defineEmits<{
87+
change: [id?: number]
88+
update: [value: string]
89+
}>()
90+
`,
91+
errors: [{ messageId: 'unexpectedSpaceBetween' }, { messageId: 'expectedSpaceAfter' }, { messageId: 'expectedSpaceAfter' }],
92+
},
93+
],
94+
})
95+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { createEslintRule } from '../utils'
2+
3+
export const RULE_NAME = 'named-tuple-spacing'
4+
export type MessageIds = 'expectedSpaceAfter' | 'unexpectedSpaceBetween' | 'unexpectedSpaceBefore'
5+
export type Options = []
6+
7+
export default createEslintRule<Options, MessageIds>({
8+
name: RULE_NAME,
9+
meta: {
10+
type: 'suggestion',
11+
docs: {
12+
description: 'Expect space before type declaration in named tuple',
13+
recommended: 'error',
14+
},
15+
fixable: 'code',
16+
schema: [],
17+
messages: {
18+
expectedSpaceAfter: 'Expected a space after the \':\'.',
19+
unexpectedSpaceBetween: 'Unexpected space between \'?\' and the \':\'.',
20+
unexpectedSpaceBefore: 'Unexpected space before the \':\'.',
21+
},
22+
},
23+
defaultOptions: [],
24+
create: (context) => {
25+
const sourceCode = context.getSourceCode()
26+
return {
27+
TSNamedTupleMember: (node) => {
28+
const code = sourceCode.text.slice(node.range[0], node.range[1])
29+
30+
const reg = /(\w+)(\s*)(\?\s*)?:(\s*)(\w+)/
31+
32+
const labelName = node.label.name
33+
const spaceBeforeColon = code.match(reg)?.[2]
34+
const optionalMark = code.match(reg)?.[3]
35+
const spacesAfterColon = code.match(reg)?.[4]
36+
const elementType = code.match(reg)?.[5]
37+
38+
function getReplaceValue() {
39+
let ret = labelName
40+
if (node.optional)
41+
ret += '?'
42+
ret += ': '
43+
ret += elementType
44+
return ret
45+
}
46+
47+
if (optionalMark?.length > 1) {
48+
context.report({
49+
node,
50+
messageId: 'unexpectedSpaceBetween',
51+
*fix(fixer) {
52+
yield fixer.replaceTextRange(node.range, code.replace(reg, getReplaceValue()))
53+
},
54+
})
55+
}
56+
57+
if (spaceBeforeColon?.length) {
58+
context.report({
59+
node,
60+
messageId: 'unexpectedSpaceBefore',
61+
*fix(fixer) {
62+
yield fixer.replaceTextRange(node.range, code.replace(reg, getReplaceValue()))
63+
},
64+
})
65+
}
66+
67+
if (spacesAfterColon.length !== 1) {
68+
context.report({
69+
node,
70+
messageId: 'expectedSpaceAfter',
71+
*fix(fixer) {
72+
yield fixer.replaceTextRange(node.range, code.replace(reg, getReplaceValue()))
73+
},
74+
})
75+
}
76+
},
77+
}
78+
},
79+
})

0 commit comments

Comments
 (0)
Please sign in to comment.