Skip to content

Commit 005037e

Browse files
committed
feat(#43): add NEXT_JS_PAGE_ROUTER_FILENAME_CASE for rule filename-naming-convention
1 parent 8ba26db commit 005037e

9 files changed

+355
-8
lines changed

docs/rules/filename-naming-convention.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Allows you to enforce a consistent naming pattern for the filename of the specif
66

77
This rule aims to format the filename of the specified file. This rule uses the glob match syntax to match target files and declare the naming pattern for the filename.
88

9-
There are six naming conventions built into this rule, including `CAMEL_CASE`, `PASCAL_CASE`, `SNAKE_CASE`, `KEBAB_CASE`, `SCREAMING_SNAKE_CASE` and `FLAT_CASE`.
9+
There are six basic naming conventions built into this rule, including `CAMEL_CASE`, `PASCAL_CASE`, `SNAKE_CASE`, `KEBAB_CASE`, `SCREAMING_SNAKE_CASE` and `FLAT_CASE`.
1010

1111
| Formatting | Name |
1212
| ----------- | ---------------------- |
@@ -17,6 +17,8 @@ There are six naming conventions built into this rule, including `CAMEL_CASE`, `
1717
| HELLO_WORLD | `SCREAMING_SNAKE_CASE` |
1818
| helloworld | `FLAT_CASE` |
1919

20+
And there is also a special naming convention for Next.js page router project, which is `NEXT_JS_PAGE_ROUTER_FILENAME_CASE`, you can use it to ensure the filename of the page router is consistent with the naming convention.
21+
2022
If the rule had been set as follows:
2123

2224
```js

lib/constants/next-js-naming-convention.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* @file Built in next js app router naming convention
2+
* @file Built in next js naming convention
33
* @author Huan Luo
44
*/
55

@@ -44,3 +44,8 @@ const NEXT_JS_FILENAME_ROUTE = `+([a-z])?(.+([a-z]))`;
4444
* @example app, [helpPageId], [...auth], [[...auth]], (auth), \@feed
4545
*/
4646
export const NEXT_JS_APP_ROUTER_CASE = `@(${KEBAB_CASE}|${NEXT_JS_DYNAMIC_SEGMENTS}|${NEXT_JS_CATCH_ALL_SEGMENTS}|${NEXT_JS_OPTIONAL_CATCH_ALL_SEGMENTS}|${NEXT_JS_ROUTE_GROUPS}|${NEXT_JS_NAMED_SLOTS}|${NEXT_JS_PRIVATE_FOLDERS}|${NEXT_JS_FILENAME_ROUTE})`;
47+
48+
/**
49+
* @example _app, _document, index, [helpPageId], [...auth], [[...auth]]
50+
*/
51+
export const NEXT_JS_PAGE_ROUTER_FILENAME_CASE = `@(_app|_document|404|500|_error|index|${NEXT_JS_DYNAMIC_SEGMENTS}|${NEXT_JS_CATCH_ALL_SEGMENTS}|${NEXT_JS_OPTIONAL_CATCH_ALL_SEGMENTS})`;

lib/rules/filename-naming-convention.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { isNotEmpty } from '../utils/utility.js';
1919
import {
2020
filenameNamingPatternValidator,
2121
globPatternValidator,
22+
nextJsFilenameNamingPatternValidator,
2223
validateNamingPatternObject,
2324
} from '../utils/validation.js';
2425

@@ -64,7 +65,9 @@ export default {
6465
const error = validateNamingPatternObject(
6566
rules,
6667
globPatternValidator,
67-
filenameNamingPatternValidator
68+
(p) =>
69+
filenameNamingPatternValidator(p) ||
70+
nextJsFilenameNamingPatternValidator(p)
6871
);
6972

7073
if (error) {

lib/utils/rule.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import micromatch from 'micromatch';
77
import * as NAMING_CONVENTION from '../constants/naming-convention.js';
8+
import * as NEXT_JS_NAMING_CONVENTION from '../constants/next-js-naming-convention.js';
89
import { PREFINED_MATCH_SYNTAX_REGEXP } from '../constants/regex.js';
910
import { isEmpty, isNil } from './utility.js';
1011

@@ -78,7 +79,9 @@ export const matchRule = (
7879
targetNamingPattern &&
7980
micromatch.isMatch(
8081
targetNaming,
81-
NAMING_CONVENTION[targetNamingPattern] || targetNamingPattern
82+
NAMING_CONVENTION[targetNamingPattern] ||
83+
NEXT_JS_NAMING_CONVENTION[targetNamingPattern] ||
84+
targetNamingPattern
8285
)
8386
) {
8487
return;

lib/utils/validation.js

+10-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
import isGlob from 'is-glob';
77
import * as BASIC_NAMING_CONVENTION from '../constants/naming-convention.js';
8-
import * as NEXT_JS_NAMING_CONVENTION from '../constants/next-js-naming-convention.js';
98
import { PREFINED_MATCH_SYNTAX_REGEXP } from '../constants/regex.js';
109
import { isObject } from './utility.js';
1110

@@ -53,8 +52,15 @@ const basicNamingPatternValidator = (namingPattern) =>
5352
* @returns {boolean} true if pattern is a valid naming pattern
5453
* @param {string} namingPattern pattern string
5554
*/
56-
const nextJsNamingPatternValidator = (namingPattern) =>
57-
Object.keys(NEXT_JS_NAMING_CONVENTION).includes(namingPattern);
55+
const nextJsFolderNamingPatternValidator = (namingPattern) =>
56+
['NEXT_JS_APP_ROUTER_CASE'].includes(namingPattern);
57+
58+
/**
59+
* @returns {boolean} true if pattern is a valid naming pattern
60+
* @param {string} namingPattern pattern string
61+
*/
62+
export const nextJsFilenameNamingPatternValidator = (namingPattern) =>
63+
['NEXT_JS_PAGE_ROUTER_FILENAME_CASE'].includes(namingPattern);
5864

5965
/**
6066
* @returns {boolean} true if pattern is a valid glob pattern
@@ -78,4 +84,4 @@ export const filenameNamingPatternValidator = (namingPattern) =>
7884
export const folderNamingPatternValidator = (namingPattern) =>
7985
globPatternValidator(namingPattern) ||
8086
basicNamingPatternValidator(namingPattern) ||
81-
nextJsNamingPatternValidator(namingPattern);
87+
nextJsFolderNamingPatternValidator(namingPattern);

tests/lib/rules/filename-naming-convention.posix.js

+140
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,146 @@ ruleTester.run(
654654
}
655655
);
656656

657+
ruleTester.run(
658+
"filename-naming-convention with option: [{ '**/*.js': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE', '**/*.jsx': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE' }]",
659+
rule,
660+
{
661+
valid: [
662+
{
663+
code: "var foo = 'bar';",
664+
filename: 'src/pages/_app.js',
665+
options: [
666+
{
667+
'**/*.js': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
668+
'**/*.jsx': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
669+
},
670+
],
671+
},
672+
{
673+
code: "var foo = 'bar';",
674+
filename: 'src/pages/_document.js',
675+
options: [
676+
{
677+
'**/*.js': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
678+
'**/*.jsx': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
679+
},
680+
],
681+
},
682+
{
683+
code: "var foo = 'bar';",
684+
filename: 'src/pages/_error.js',
685+
options: [
686+
{
687+
'**/*.js': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
688+
'**/*.jsx': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
689+
},
690+
],
691+
},
692+
{
693+
code: "var foo = 'bar';",
694+
filename: 'src/pages/404.js',
695+
options: [
696+
{
697+
'**/*.js': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
698+
'**/*.jsx': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
699+
},
700+
],
701+
},
702+
{
703+
code: "var foo = 'bar';",
704+
filename: 'src/pages/500.js',
705+
options: [
706+
{
707+
'**/*.js': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
708+
'**/*.jsx': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
709+
},
710+
],
711+
},
712+
{
713+
code: "var foo = 'bar';",
714+
filename: 'src/pages/blog/index.js',
715+
options: [
716+
{
717+
'**/*.js': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
718+
'**/*.jsx': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
719+
},
720+
],
721+
},
722+
{
723+
code: "var foo = 'bar';",
724+
filename: 'src/pages/blog/[post].js',
725+
options: [
726+
{
727+
'**/*.js': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
728+
'**/*.jsx': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
729+
},
730+
],
731+
},
732+
{
733+
code: "var foo = 'bar';",
734+
filename: 'src/pages/blog/[blogPost].js',
735+
options: [
736+
{
737+
'**/*.js': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
738+
'**/*.jsx': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
739+
},
740+
],
741+
},
742+
{
743+
code: "var foo = 'bar';",
744+
filename: 'src/pages/blog/[[...slug]].js',
745+
options: [
746+
{
747+
'**/*.js': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
748+
'**/*.jsx': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
749+
},
750+
],
751+
},
752+
{
753+
code: "var foo = 'bar';",
754+
filename: 'src/pages/blog/[...params].js',
755+
options: [
756+
{
757+
'**/*.js': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
758+
'**/*.jsx': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
759+
},
760+
],
761+
},
762+
{
763+
code: "var foo = 'bar';",
764+
filename: 'src/pages/blog/[category]/[post].js',
765+
options: [
766+
{
767+
'**/*.js': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
768+
'**/*.jsx': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
769+
},
770+
],
771+
},
772+
],
773+
774+
invalid: [
775+
{
776+
code: "var foo = 'bar';",
777+
filename: 'src/utils/CALCULATE_PRICE.js',
778+
options: [
779+
{
780+
'**/*.js': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
781+
'**/*.jsx': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
782+
},
783+
],
784+
errors: [
785+
{
786+
message:
787+
'The filename "CALCULATE_PRICE.js" does not match the "NEXT_JS_PAGE_ROUTER_FILENAME_CASE" pattern',
788+
column: 1,
789+
line: 1,
790+
},
791+
],
792+
},
793+
],
794+
}
795+
);
796+
657797
ruleTester.run(
658798
"filename-naming-convention with option: [{ '**/*.js': '__+([a-z])', '**/*.jsx': '__+([a-z])' }]",
659799
rule,

tests/lib/rules/filename-naming-convention.windows.js

+140
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,146 @@ ruleTester.run(
676676
}
677677
);
678678

679+
ruleTester.run(
680+
"filename-naming-convention with option on Windows: [{ '**/*.js': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE', '**/*.jsx': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE' }]",
681+
rule,
682+
{
683+
valid: [
684+
{
685+
code: "var foo = 'bar';",
686+
filename: 'src\\pages\\_app.js',
687+
options: [
688+
{
689+
'**/*.js': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
690+
'**/*.jsx': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
691+
},
692+
],
693+
},
694+
{
695+
code: "var foo = 'bar';",
696+
filename: 'src\\pages\\_document.js',
697+
options: [
698+
{
699+
'**/*.js': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
700+
'**/*.jsx': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
701+
},
702+
],
703+
},
704+
{
705+
code: "var foo = 'bar';",
706+
filename: 'src\\pages\\_error.js',
707+
options: [
708+
{
709+
'**/*.js': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
710+
'**/*.jsx': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
711+
},
712+
],
713+
},
714+
{
715+
code: "var foo = 'bar';",
716+
filename: 'src\\pages\\404.js',
717+
options: [
718+
{
719+
'**/*.js': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
720+
'**/*.jsx': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
721+
},
722+
],
723+
},
724+
{
725+
code: "var foo = 'bar';",
726+
filename: 'src\\pages\\500.js',
727+
options: [
728+
{
729+
'**/*.js': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
730+
'**/*.jsx': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
731+
},
732+
],
733+
},
734+
{
735+
code: "var foo = 'bar';",
736+
filename: 'src\\pages\\blog\\index.js',
737+
options: [
738+
{
739+
'**/*.js': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
740+
'**/*.jsx': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
741+
},
742+
],
743+
},
744+
{
745+
code: "var foo = 'bar';",
746+
filename: 'src\\pages\\blog\\[post].js',
747+
options: [
748+
{
749+
'**/*.js': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
750+
'**/*.jsx': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
751+
},
752+
],
753+
},
754+
{
755+
code: "var foo = 'bar';",
756+
filename: 'src\\pages\\blog\\[blogPost].js',
757+
options: [
758+
{
759+
'**/*.js': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
760+
'**/*.jsx': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
761+
},
762+
],
763+
},
764+
{
765+
code: "var foo = 'bar';",
766+
filename: 'src\\pages\\blog\\[[...slug]].js',
767+
options: [
768+
{
769+
'**/*.js': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
770+
'**/*.jsx': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
771+
},
772+
],
773+
},
774+
{
775+
code: "var foo = 'bar';",
776+
filename: 'src\\pages\\blog\\[...params].js',
777+
options: [
778+
{
779+
'**/*.js': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
780+
'**/*.jsx': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
781+
},
782+
],
783+
},
784+
{
785+
code: "var foo = 'bar';",
786+
filename: 'src\\pages\\blog\\[category]\\[post].js',
787+
options: [
788+
{
789+
'**/*.js': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
790+
'**/*.jsx': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
791+
},
792+
],
793+
},
794+
],
795+
796+
invalid: [
797+
{
798+
code: "var foo = 'bar';",
799+
filename: 'src\\utils\\CALCULATE_PRICE.js',
800+
options: [
801+
{
802+
'**/*.js': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
803+
'**/*.jsx': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE',
804+
},
805+
],
806+
errors: [
807+
{
808+
message:
809+
'The filename "CALCULATE_PRICE.js" does not match the "NEXT_JS_PAGE_ROUTER_FILENAME_CASE" pattern',
810+
column: 1,
811+
line: 1,
812+
},
813+
],
814+
},
815+
],
816+
}
817+
);
818+
679819
ruleTester.run(
680820
"filename-naming-convention with option on Windows: [{ '**/*.js': 'CAMEL_CASE' }, { ignoreMiddleExtensions: true }]",
681821
rule,

0 commit comments

Comments
 (0)