@@ -34,12 +34,14 @@ pub struct FilenameCaseConfig {
34
34
/// Whether pascal case is allowed.
35
35
pascal_case : bool ,
36
36
ignore : Option < Regex > ,
37
+ multi_extensions : bool ,
37
38
}
38
39
39
40
declare_oxc_lint ! (
40
41
/// ### What it does
41
42
///
42
- /// Enforces specific case styles for filenames. By default, kebab case is enforced.
43
+ /// Enforces a consistent case style for filenames to improve project organization and maintainability.
44
+ /// By default, `kebab-case` is enforced, but other styles can be configured.
43
45
///
44
46
/// ### Why is this bad?
45
47
///
@@ -77,8 +79,6 @@ declare_oxc_lint!(
77
79
///
78
80
/// ### Options
79
81
///
80
- /// Use `kebabCase` as the default option.
81
- ///
82
82
/// #### case
83
83
///
84
84
/// `{ type: 'kebabCase' | 'camelCase' | 'snakeCase' | 'pascalCase' }`
@@ -112,7 +112,9 @@ declare_oxc_lint!(
112
112
///
113
113
/// #### ignore
114
114
///
115
- /// `{ type: String (must be a valid regular expression) }`
115
+ /// `{ type: string }`
116
+ ///
117
+ /// Specifies a regular expression pattern for filenames that should be ignored by this rule.
116
118
///
117
119
/// You can set the `ignore` option like this:
118
120
/// ```json
@@ -123,18 +125,28 @@ declare_oxc_lint!(
123
125
/// }
124
126
/// ]
125
127
/// ```
128
+ ///
129
+ /// #### multipleFileExtensions
130
+ ///
131
+ /// `{ type: boolean, default: true }`
132
+ ///
133
+ /// Whether to treat additional, `.`-separated parts of a filename as parts of the extension rather than parts of the filename.
126
134
FilenameCase ,
127
135
unicorn,
128
136
style
129
137
) ;
130
138
131
139
impl Rule for FilenameCase {
132
140
fn from_configuration ( value : serde_json:: Value ) -> Self {
133
- let mut config = FilenameCaseConfig :: default ( ) ;
141
+ let mut config = FilenameCaseConfig { multi_extensions : true , .. Default :: default ( ) } ;
134
142
135
143
if let Some ( value) = value. get ( 0 ) {
136
- if let Some ( Value :: String ( pat) ) = value. get ( "ignore" ) {
137
- config. ignore = RegexBuilder :: new ( pat) . build ( ) . ok ( ) ;
144
+ if let Some ( Value :: String ( val) ) = value. get ( "ignore" ) {
145
+ config. ignore = RegexBuilder :: new ( val) . build ( ) . ok ( ) ;
146
+ }
147
+
148
+ if let Some ( Value :: Bool ( val) ) = value. get ( "multipleFileExtensions" ) {
149
+ config. multi_extensions = * val;
138
150
}
139
151
140
152
if let Some ( Value :: String ( s) ) = value. get ( "case" ) {
@@ -176,7 +188,12 @@ impl Rule for FilenameCase {
176
188
return ;
177
189
}
178
190
179
- let filename = raw_filename. rsplit_once ( '.' ) . map ( |( before, _) | before) ;
191
+ let filename = if self . multi_extensions {
192
+ raw_filename. split ( '.' ) . next ( )
193
+ } else {
194
+ raw_filename. rsplit_once ( '.' ) . map ( |( before, _) | before)
195
+ } ;
196
+
180
197
let filename = filename. unwrap_or ( raw_filename) ;
181
198
let trimmed_filename = filename. trim_matches ( '_' ) ;
182
199
@@ -286,29 +303,29 @@ fn test() {
286
303
test_case( "src/foo/fooBar.js" , "camelCase" ) ,
287
304
test_case( "src/foo/bar.test.js" , "camelCase" ) ,
288
305
test_case( "src/foo/fooBar.test.js" , "camelCase" ) ,
289
- // test_case("src/foo/fooBar.test-utils.js", "camelCase"),
290
- // test_case("src/foo/fooBar.test_utils.js", "camelCase"),
306
+ test_case( "src/foo/fooBar.test-utils.js" , "camelCase" ) ,
307
+ test_case( "src/foo/fooBar.test_utils.js" , "camelCase" ) ,
291
308
test_case( "src/foo/.test_utils.js" , "camelCase" ) ,
292
309
test_case( "src/foo/foo.js" , "snakeCase" ) ,
293
310
test_case( "src/foo/foo_bar.js" , "snakeCase" ) ,
294
311
test_case( "src/foo/foo.test.js" , "snakeCase" ) ,
295
312
test_case( "src/foo/foo_bar.test.js" , "snakeCase" ) ,
296
313
test_case( "src/foo/foo_bar.test_utils.js" , "snakeCase" ) ,
297
- // test_case("src/foo/foo_bar.test-utils.js", "snakeCase"),
314
+ test_case( "src/foo/foo_bar.test-utils.js" , "snakeCase" ) ,
298
315
test_case( "src/foo/.test-utils.js" , "snakeCase" ) ,
299
316
test_case( "src/foo/foo.js" , "kebabCase" ) ,
300
317
test_case( "src/foo/foo-bar.js" , "kebabCase" ) ,
301
318
test_case( "src/foo/foo.test.js" , "kebabCase" ) ,
302
319
test_case( "src/foo/foo-bar.test.js" , "kebabCase" ) ,
303
320
test_case( "src/foo/foo-bar.test-utils.js" , "kebabCase" ) ,
304
- // test_case("src/foo/foo-bar.test_utils.js", "kebabCase"),
321
+ test_case( "src/foo/foo-bar.test_utils.js" , "kebabCase" ) ,
305
322
test_case( "src/foo/.test_utils.js" , "kebabCase" ) ,
306
323
test_case( "src/foo/Foo.js" , "pascalCase" ) ,
307
324
test_case( "src/foo/FooBar.js" , "pascalCase" ) ,
308
325
test_case( "src/foo/Foo.test.js" , "pascalCase" ) ,
309
326
test_case( "src/foo/FooBar.test.js" , "pascalCase" ) ,
310
- // test_case("src/foo/FooBar.test-utils.js", "pascalCase"),
311
- // test_case("src/foo/FooBar.test_utils.js", "pascalCase"),
327
+ test_case( "src/foo/FooBar.test-utils.js" , "pascalCase" ) ,
328
+ test_case( "src/foo/FooBar.test_utils.js" , "pascalCase" ) ,
312
329
test_case( "src/foo/.test_utils.js" , "pascalCase" ) ,
313
330
test_case( "spec/iss47Spec.js" , "camelCase" ) ,
314
331
test_case( "spec/iss47Spec100.js" , "camelCase" ) ,
@@ -355,6 +372,14 @@ fn test() {
355
372
"src/foo/BAR.js" ,
356
373
serde_json:: json!( [ { "case" : "kebabCase" , "ignore" : r"FOO.js|BAR.js" } ] ) ,
357
374
) ,
375
+ test_case_with_options(
376
+ "src/foo/fooBar.testUtils.js" ,
377
+ serde_json:: json!( [ { "case" : "camelCase" , "multipleFileExtensions" : false } ] ) ,
378
+ ) ,
379
+ test_case_with_options(
380
+ "src/foo/foo_bar.test_utils.js" ,
381
+ serde_json:: json!( [ { "case" : "snakeCase" , "multipleFileExtensions" : false } ] ) ,
382
+ ) ,
358
383
] ;
359
384
360
385
let fail = vec ! [
@@ -392,6 +417,14 @@ fn test() {
392
417
"src/foo/FOOBAR.js" ,
393
418
serde_json:: json!( [ { "case" : "kebabCase" , "ignore" : r"foobar.js" } ] ) ,
394
419
) ,
420
+ test_case_with_options(
421
+ "src/foo/fooBar.TestUtils.js" ,
422
+ serde_json:: json!( [ { "case" : "camelCase" , "multipleFileExtensions" : false } ] ) ,
423
+ ) ,
424
+ test_case_with_options(
425
+ "src/foo/foo_bar.test-utils.js" ,
426
+ serde_json:: json!( [ { "case" : "snakeCase" , "multipleFileExtensions" : false } ] ) ,
427
+ ) ,
395
428
] ;
396
429
397
430
Tester :: new ( FilenameCase :: NAME , FilenameCase :: PLUGIN , pass, fail) . test_and_snapshot ( ) ;
0 commit comments