Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(useFilenamingConvention): support dynamic route filenames #3466

Merged
merged 1 commit into from
Jul 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,14 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b

Contributed by @Conaclos

- [useFilenamingConvention](https://biomejs.dev/linter/rules/use-filenaming-convention) now supports Next.js/Nuxt/Astro dynamic routes ([#3465](https://github.com/biomejs/biome/issues/3465)).

[Next.js](https://nextjs.org/docs/pages/building-your-application/routing/dynamic-routes#catch-all-segments), [SolidStart](https://docs.solidjs.com/solid-start/building-your-application/routing#renaming-index), [Nuxt](https://nuxt.com/docs/guide/directory-structure/server#catch-all-route), and [Astro](https://docs.astro.build/en/guides/routing/#rest-parameters) support dynamic routes such as `[...slug].js` and `[[...slug]].js`.

Biome now recognizes this syntax. `slug` must contain only alphanumeric characters.

Contributed by @Conaclos

#### Bug fixes

- Don't request alt text for elements hidden from assistive technologies ([#3316](https://github.com/biomejs/biome/issues/3316)). Contributed by @robintown
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

84 changes: 64 additions & 20 deletions crates/biome_js_analyze/src/lint/style/use_filenaming_convention.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ declare_lint_rule! {
/// The convention of prefixing a filename with a plus sign is used by
/// [Sveltekit](https://kit.svelte.dev/docs/routing#page) and [Vike](https://vike.dev/route).
///
/// Also, the rule supports dynamic route syntaxes of [Next.js](https://nextjs.org/docs/pages/building-your-application/routing/dynamic-routes#catch-all-segments), [SolidStart](https://docs.solidjs.com/solid-start/building-your-application/routing#renaming-index), [Nuxt](https://nuxt.com/docs/guide/directory-structure/server#catch-all-route), and [Astro](https://docs.astro.build/en/guides/routing/#rest-parameters).
/// For example `[...slug].js` and `[[...slug]].js` are valid filenames.
///
/// By default, the rule ensures that the filename is either in [`camelCase`], [`kebab-case`], [`snake_case`],
/// or equal to the name of one export in the file.
///
Expand Down Expand Up @@ -126,28 +129,69 @@ impl Rule for UseFilenamingConvention {
if options.require_ascii && !file_name.is_ascii() {
return Some(FileNamingConventionState::Ascii);
}
let allowed_cases = options.filename_cases.cases();
let mut splitted = file_name.split('.');
let name = splitted.next()?;
let name = if name.is_empty() {
// The filename starts with a dot
splitted.next()?
} else if let Some(stripped_name) = name.strip_prefix('+') {
// Support [Sveltekit](https://kit.svelte.dev/docs/routing#page) and
// [Vike](https://vike.dev/route) routing conventions
// where page name starts with `+`.
stripped_name
let first_char = file_name.bytes().next()?;
let (name, mut extensions) = if matches!(first_char, b'(' | b'[') {
// Support [Next.js](https://nextjs.org/docs/pages/building-your-application/routing/dynamic-routes#catch-all-segments),
// [SolidStart](https://docs.solidjs.com/solid-start/building-your-application/routing#renaming-index),
// [Nuxt](https://nuxt.com/docs/guide/directory-structure/server#catch-all-route),
// and [Astro](https://docs.astro.build/en/guides/routing/#rest-parameters)
// dynamic routes. Some examples:
//
// - `(slug).js`
// - `[slug].js`
// - `[[slug]].js`
// - `[...slug].js`
// - `[[...slug]].js`
let count = if file_name.starts_with("[[") { 2 } else { 1 };
let to_split = if first_char != b'(' && file_name[count..].starts_with("...") {
&file_name[count + 3..]
} else {
&file_name[count..]
};
let mut split = to_split.split('.');
let Some(name) = split.next() else {
return Some(FileNamingConventionState::Filename);
};
let ends = if count == 2 {
"]]"
} else if first_char == b'[' {
"]"
} else {
")"
};
if !name.ends_with(ends)
|| !name[..name.len() - count]
.chars()
.all(|c| c.is_alphanumeric())
{
return Some(FileNamingConventionState::Filename);
}
("", split)
} else {
name
// Support UNIX hidden files (filenames starting with a dot).
//
// Support [Sveltekit](https://kit.svelte.dev/docs/routing#page) and
// [Vike](https://vike.dev/route) routing conventions where page name starts with `+`.
let file_name = if matches!(first_char, b'.' | b'+') {
&file_name[1..]
} else {
file_name
};
let mut split = file_name.split('.');
let Some(name) = split.next().filter(|name| !name.is_empty()) else {
return Some(FileNamingConventionState::Filename);
};
(name, split)
};
// Check extension case
for extension in splitted {
let case = Case::identify(extension, true);
if case != Case::Lower {
return Some(FileNamingConventionState::Extension);
}
if extensions.any(|extension| Case::identify(extension, true) != Case::Lower) {
return Some(FileNamingConventionState::Extension);
}
if name.is_empty() {
return None;
}
// Check filename case
let allowed_cases = options.filename_cases.cases();
if !allowed_cases.is_empty() {
let trimmed_name = name.trim_matches('_');
let case = Case::identify(trimmed_name, options.strict_case);
Expand Down Expand Up @@ -198,11 +242,11 @@ impl Rule for UseFilenamingConvention {
.collect::<SmallVec<[_; 3]>>()
.join(" or ")
};
let mut splitted = file_name.split('.');
let name = splitted.next()?;
let mut split = file_name.split('.');
let name = split.next()?;
let name = if name.is_empty() {
// The filename starts with a dot
splitted.next()?
split.next()?
} else if let Some(stripped_name) = name.strip_prefix('+') {
stripped_name
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
source: crates/biome_js_analyze/tests/spec_tests.rs
expression: (slug1).js
---
# Input
```jsx

```
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
source: crates/biome_js_analyze/tests/spec_tests.rs
expression: "[...slug2].js"
---
# Input
```jsx

```
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
source: crates/biome_js_analyze/tests/spec_tests.rs
expression: "[[...slug3]].js"
---
# Input
```jsx

```
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
source: crates/biome_js_analyze/tests/spec_tests.rs
expression: "[slug4].js"
---
# Input
```jsx

```
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
source: crates/biome_json_parser/tests/spec_test.rs
expression: snapshot
---

## Input

```json
Expand Down Expand Up @@ -48,7 +47,7 @@ JsonRoot {
## Diagnostics

```
number_0.3e+.json:1:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
number_0.3e_plus.json:1:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× Missing exponent

Expand All @@ -61,5 +60,3 @@ number_0.3e+.json:1:2 parse ━━━━━━━━━━━━━━━━━
│ ^

```


Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
source: crates/biome_json_parser/tests/spec_test.rs
expression: snapshot
---

## Input

```json
Expand Down Expand Up @@ -48,7 +47,7 @@ JsonRoot {
## Diagnostics

```
number_0_capital_E+.json:1:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
number_0_capital_E_plus.json:1:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× Missing exponent

Expand All @@ -61,5 +60,3 @@ number_0_capital_E+.json:1:2 parse ━━━━━━━━━━━━━━━
│ ^

```


Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
source: crates/biome_json_parser/tests/spec_test.rs
expression: snapshot
---

## Input

```json
Expand Down Expand Up @@ -48,7 +47,7 @@ JsonRoot {
## Diagnostics

```
number_0e+.json:1:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
number_0e+_missing_digit.json:1:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× Missing exponent

Expand All @@ -61,5 +60,3 @@ number_0e+.json:1:2 parse ━━━━━━━━━━━━━━━━━━
│ ^

```


Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
source: crates/biome_json_parser/tests/spec_test.rs
expression: snapshot
---

## Input

```json
Expand Down Expand Up @@ -48,7 +47,7 @@ JsonRoot {
## Diagnostics

```
number_1.0e-.json:1:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
number_1.0e-_missing_digit.json:1:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× Missing exponent

Expand All @@ -61,5 +60,3 @@ number_1.0e-.json:1:2 parse ━━━━━━━━━━━━━━━━━
│ ^

```


Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
source: crates/biome_json_parser/tests/spec_test.rs
expression: snapshot
---
## Input

```json
[1.0e-]
```


## AST

```
JsonRoot {
bom_token: missing (optional),
value: JsonArrayValue {
l_brack_token: [email protected] "[" [] [],
elements: JsonArrayElementList [
JsonBogusValue {
items: [
[email protected] "1.0e-" [] [],
],
},
],
r_brack_token: [email protected] "]" [] [],
},
eof_token: [email protected] "" [] [],
}
```

## CST

```
0: [email protected]
0: (empty)
1: [email protected]
0: [email protected] "[" [] []
1: [email protected]
0: [email protected]
0: [email protected] "1.0e-" [] []
2: [email protected] "]" [] []
2: [email protected] "" [] []

```

## Diagnostics

```
number_1.0e_minus.json:1:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× Missing exponent

> 1 │ [1.0e-]
│ ^^^^^

i Expected a digit as the exponent

> 1 │ [1.0e-]
│ ^

```
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,14 @@ JsonRoot {
## Diagnostics

```
number_-NaN.json:1:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
number_neg_NaN.json:1:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× Minus must be followed by a digit

> 1 │ [-NaN]
│ ^

number_-NaN.json:1:3 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
number_neg_NaN.json:1:3 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× expected `,` but instead found `NaN`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
source: crates/biome_json_parser/tests/spec_test.rs
expression: snapshot
---

## Input

```json
Expand Down Expand Up @@ -55,14 +54,14 @@ JsonRoot {
## Diagnostics

```
number_+1.json:1:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
number_plus_1.json:1:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× unexpected character `+`

> 1 │ [+1]
│ ^

number_+1.json:1:3 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
number_plus_1.json:1:3 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× expected `,` but instead found `1`

Expand All @@ -72,5 +71,3 @@ number_+1.json:1:3 parse ━━━━━━━━━━━━━━━━━━
i Remove 1

```


Loading