@@ -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 = / s r c [ / \\ ] a p p [ / \\ ] /
11+ const FILE_TYPE_PATTERN = / ( p a g e | l a y o u t | 4 0 4 ) \. [ j t ] s x ? $ /
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+
918export 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 ( ! / s r c [ / \\ ] a p p [ / \\ ] / . test ( filename ) ) return { }
33- const type = / p a g e \. [ j | t ] s x ? $ / . test ( filename )
34- ? 'page'
35- : / l a y o u t \. [ j | t ] s x ? $ / . test ( filename )
36- ? 'layout'
37- : / 4 0 4 \. [ j | t ] s x ? $ / . 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 }
0 commit comments