Skip to content

Commit 5f14563

Browse files
committed
Rm vitest
1 parent 8cdcaa4 commit 5f14563

4 files changed

Lines changed: 147 additions & 178 deletions

File tree

src/rules/app-page/index.ts

Lines changed: 43 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ const createRule = ESLintUtils.RuleCreator(
66
`https://github.com/dev-five-git/devup/tree/main/packages/eslint-plugin/src/rules/${name}`,
77
)
88

9+
// Pre-compiled regex patterns
10+
const APP_DIR_PATTERN = /src[/\\]app[/\\]/
11+
const FILE_TYPE_PATTERN = /(page|layout|404)\.[jt]sx?$/
12+
const PATH_PARAMS_PATTERN = /\[.*?]/g
13+
14+
function generateParamsType(pathParams: string[]): string {
15+
return `{params}:{params:Promise<{${pathParams.map((param) => `${param.slice(1, -1)}:string`).join(';')}}>}`
16+
}
17+
918
export const appPage = createRule({
1019
name: 'app-page',
1120
defaultOptions: [],
@@ -29,86 +38,77 @@ export const appPage = createRule({
2938
create(context) {
3039
const filename = relative(context.cwd, context.physicalFilename)
3140

32-
if (!/src[/\\]app[/\\]/.test(filename)) return {}
33-
const type = /page\.[j|t]sx?$/.test(filename)
34-
? 'page'
35-
: /layout\.[j|t]sx?$/.test(filename)
36-
? 'layout'
37-
: /404\.[j|t]sx?$/.test(filename)
38-
? '404'
39-
: 'component'
40-
if (type === 'component') return {}
41+
if (!APP_DIR_PATTERN.test(filename)) return {}
42+
43+
const fileTypeMatch = FILE_TYPE_PATTERN.exec(filename)
44+
if (!fileTypeMatch) return {}
45+
46+
const type = fileTypeMatch[1] as 'page' | 'layout' | '404'
4147

4248
const functionSuffix = type === 'layout' ? 'Layout' : 'Page'
43-
const pathParams = filename.match(/\[.*?]/g) ?? []
49+
const pathParams = filename.match(PATH_PARAMS_PATTERN) ?? []
4450
// Ignore when only locale param exists
4551
const onlyLocale = pathParams.length === 1 && pathParams[0] === '[locale]'
4652

53+
const hasPathParams = pathParams.length > 0
54+
const requiresParams = hasPathParams && !onlyLocale
55+
const paramsType = hasPathParams ? generateParamsType(pathParams) : ''
56+
4757
let ok = false
4858
return {
4959
ExportDefaultDeclaration(defaultExport) {
5060
ok = true
5161
const declaration = defaultExport.declaration
5262
if (declaration.type !== 'FunctionDeclaration') return
63+
5364
if (
5465
declaration.id?.name &&
5566
!declaration.id.name.endsWith(functionSuffix)
5667
) {
57-
const func = declaration.id
5868
context.report({
59-
node: func,
69+
node: declaration.id,
6070
messageId: 'nameOfPageOrLayoutComponentShouldHaveSuffix',
61-
fix(fixer) {
62-
return fixer.replaceText(func, func.name + functionSuffix)
63-
},
71+
fix: (fixer) =>
72+
fixer.replaceText(
73+
declaration.id!,
74+
declaration.id!.name + functionSuffix,
75+
),
6476
})
6577
}
78+
6679
if (
6780
declaration.id &&
68-
pathParams.length &&
69-
declaration.params.length === 0 &&
70-
!onlyLocale
81+
requiresParams &&
82+
declaration.params.length === 0
7183
) {
7284
context.report({
7385
node: declaration,
7486
messageId: 'pathParamsShouldExist',
75-
fix(fixer) {
76-
return fixer.replaceTextRange(
87+
fix: (fixer) =>
88+
fixer.replaceTextRange(
7789
[
7890
declaration.id!.range[1],
79-
declaration.returnType
80-
? declaration.returnType.range[0]
81-
: declaration.body.range[0],
91+
declaration.returnType?.range[0] ?? declaration.body.range[0],
8292
],
83-
`({params}:{params:Promise<{${pathParams
84-
.map((param) => `${param.slice(1, -1)}:string`)
85-
.join(';')}}>})`,
86-
)
87-
},
93+
`(${paramsType})`,
94+
),
8895
})
8996
}
90-
return
9197
},
9298
'Program:exit'(program) {
93-
// Auto-generate if source code is empty
9499
if (ok) return
100+
101+
const functionName = type === '404' ? 'NotFoundPage' : functionSuffix
102+
const prefix = context.sourceCode.text.trim().length ? '\n' : ''
103+
95104
context.report({
96105
node: program,
97106
messageId: 'pageOrLayoutComponentShouldDefaultExport',
98-
fix(fixer) {
99-
return fixer.insertTextAfter(
107+
fix: (fixer) =>
108+
fixer.insertTextAfter(
100109
program,
101-
`${context.sourceCode.text.trim().length ? '\n' : ''}export default function ` +
102-
(type === '404' ? 'NotFoundPage' : functionSuffix) +
103-
`(${
104-
pathParams.length
105-
? `{params}:{params:Promise<{${pathParams
106-
.map((param) => `${param.slice(1, -1)}:string`)
107-
.join(';')}}>}`
108-
: ''
109-
}){return <></>}`,
110-
)
111-
},
110+
`${prefix}export default function ${functionName}(${paramsType}){return <></>}`,
111+
),
112112
})
113113
},
114114
}

src/rules/component-interface/index.ts

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ const createRule = ESLintUtils.RuleCreator(
55
`https://github.com/dev-five-git/devup/tree/main/packages/eslint-plugin/src/rules/${name}`,
66
)
77

8+
const PASCAL_CASE_PATTERN = /^[A-Z]/
9+
const EXPORTABLE_PARENT_TYPES = new Set([
10+
'Program',
11+
'ExportNamedDeclaration',
12+
'ExportDefaultDeclaration',
13+
])
14+
815
export const componentInterface = createRule({
916
name: 'component-interface',
1017
defaultOptions: [],
@@ -22,41 +29,38 @@ export const componentInterface = createRule({
2229
},
2330
},
2431
create(context) {
25-
const filename = context.physicalFilename
26-
27-
if (!filename.endsWith('.tsx')) return {}
32+
if (!context.physicalFilename.endsWith('.tsx')) return {}
2833

2934
return {
3035
FunctionDeclaration(node) {
3136
const funcName = node.id?.name
37+
if (!funcName || !PASCAL_CASE_PATTERN.test(funcName)) return
3238

39+
const firstParam = node.params[0]
3340
if (
34-
funcName &&
35-
node.params.length === 1 &&
36-
node.params[0].type === 'ObjectPattern' &&
37-
!node.params[0].typeAnnotation &&
38-
node.params[0].properties.length === 0 &&
39-
(node.parent.type === 'Program' ||
40-
node.parent.type === 'ExportNamedDeclaration' ||
41-
node.parent.type === 'ExportDefaultDeclaration') &&
42-
/^[A-Z]/.test(funcName)
41+
node.params.length !== 1 ||
42+
firstParam.type !== 'ObjectPattern' ||
43+
firstParam.typeAnnotation ||
44+
firstParam.properties.length !== 0 ||
45+
!EXPORTABLE_PARENT_TYPES.has(node.parent.type)
4346
) {
44-
context.report({
45-
node,
46-
messageId:
47-
'componentPropsShouldHaveTypeAnnotationWhenEmptyObjectPattern',
48-
fix(fixer) {
49-
return [
50-
fixer.insertTextAfter(node.params[0], `:${funcName}Props`),
51-
// Insert before the line
52-
fixer.insertTextBefore(
53-
node.parent.type === 'Program' ? node : node.parent,
54-
`interface ${funcName}Props{}\n`,
55-
),
56-
]
57-
},
58-
})
47+
return
5948
}
49+
50+
const insertTarget = node.parent.type === 'Program' ? node : node.parent
51+
52+
context.report({
53+
node,
54+
messageId:
55+
'componentPropsShouldHaveTypeAnnotationWhenEmptyObjectPattern',
56+
fix: (fixer) => [
57+
fixer.insertTextAfter(firstParam, `:${funcName}Props`),
58+
fixer.insertTextBefore(
59+
insertTarget,
60+
`interface ${funcName}Props{}\n`,
61+
),
62+
],
63+
})
6064
},
6165
}
6266
},

0 commit comments

Comments
 (0)