Skip to content

Commit 5c3d542

Browse files
authored
fix(linter): fix aliased paths in useImportExtensions (#7597)
1 parent f18dac1 commit 5c3d542

File tree

18 files changed

+248
-25
lines changed

18 files changed

+248
-25
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@biomejs/biome": patch
3+
---
4+
5+
Fixed [#6432](https://github.com/biomejs/biome/issues/6432): [`useImportExtensions`](https://biomejs.dev/linter/rules/use-import-extensions/) now works correctly with aliased paths.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@biomejs/biome": patch
3+
---
4+
5+
Fixed an issue where `package.json` manifests would not be correctly discovered
6+
when evaluating files in the same directory.

crates/biome_js_analyze/src/lint/correctness/use_import_extensions.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ impl Rule for UseImportExtensions {
147147
.get_import_path_by_js_node(node)
148148
.and_then(JsImportPath::as_path)?;
149149

150-
get_extensionless_import(node, resolved_path, force_js_extensions)
150+
get_extensionless_import(node, resolved_path, ctx, force_js_extensions)
151151
}
152152

153153
fn diagnostic(_: &RuleContext<Self>, state: &Self::State) -> Option<RuleDiagnostic> {
@@ -200,6 +200,7 @@ pub struct UseImportExtensionsState {
200200
fn get_extensionless_import(
201201
node: &AnyJsImportLike,
202202
resolved_path: &Utf8Path,
203+
ctx: &RuleContext<UseImportExtensions>,
203204
force_js_extensions: bool,
204205
) -> Option<UseImportExtensionsState> {
205206
let module_name_token = node.module_name_token()?;
@@ -212,7 +213,17 @@ fn get_extensionless_import(
212213
first_component,
213214
Utf8Component::CurDir | Utf8Component::ParentDir
214215
) {
215-
return None;
216+
// TypeScript path aliases should still be considered.
217+
// The same does *not* apply for `package.json` aliases, because
218+
// extensions are not automatically applied to those.
219+
let matches_path_alias = ctx
220+
.project_layout()
221+
.query_tsconfig_for_path(ctx.file_path(), |tsconfig| {
222+
tsconfig.matches_path_alias(path.as_str())
223+
})?;
224+
if !matches_path_alias {
225+
return None;
226+
}
216227
}
217228

218229
let resolved_stem = resolved_path.file_stem();
@@ -291,7 +302,7 @@ fn get_extensionless_import(
291302
};
292303

293304
if let Some(sub_ext) = sub_extension {
294-
new_path.set_extension(format!("{sub_ext}.{extension}",));
305+
new_path.set_extension(format!("{sub_ext}.{extension}"));
295306
} else {
296307
new_path.set_extension(extension);
297308
}

crates/biome_js_analyze/src/services/module_graph.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@ use biome_analyze::{
33
RuleMetadata, ServiceBag, ServicesDiagnostic, SyntaxVisitor,
44
};
55
use biome_module_graph::{JsModuleInfo, ModuleGraph};
6+
use biome_project_layout::ProjectLayout;
67
use biome_rowan::{AstNode, Language, SyntaxNode, TextRange};
78
use camino::Utf8Path;
89
use std::sync::Arc;
910

1011
#[derive(Debug, Clone)]
11-
pub struct ModuleGraphService(Arc<ModuleGraph>);
12+
pub struct ModuleGraphService(Arc<ModuleGraph>, Arc<ProjectLayout>);
1213

1314
impl ModuleGraphService {
1415
pub fn module_graph(&self) -> &ModuleGraph {
@@ -18,6 +19,10 @@ impl ModuleGraphService {
1819
pub fn module_info_for_path(&self, path: &Utf8Path) -> Option<JsModuleInfo> {
1920
self.0.module_info_for_path(path)
2021
}
22+
23+
pub fn project_layout(&self) -> &ProjectLayout {
24+
self.1.as_ref()
25+
}
2126
}
2227

2328
impl FromServices for ModuleGraphService {
@@ -41,7 +46,11 @@ impl FromServices for ModuleGraphService {
4146
.get_service()
4247
.ok_or_else(|| ServicesDiagnostic::new(rule_key.rule_name(), &["ModuleGraph"]))?;
4348

44-
Ok(Self(module_graph.clone()))
49+
let project_layout: &Arc<ProjectLayout> = services
50+
.get_service()
51+
.ok_or_else(|| ServicesDiagnostic::new(rule_key.rule_name(), &["ProjectLayout"]))?;
52+
53+
Ok(Self(module_graph.clone(), project_layout.clone()))
4554
}
4655
}
4756

crates/biome_js_analyze/tests/spec_tests.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use biome_test_utils::{
1515
CheckActionType, assert_diagnostics_expectation_comment, assert_errors_are_absent,
1616
code_fix_to_string, create_analyzer_options, diagnostic_to_string,
1717
has_bogus_nodes_or_empty_slots, module_graph_for_test_file, parse_test_path,
18-
project_layout_with_node_manifest, register_leak_checker, scripts_from_json,
18+
project_layout_for_test_file, register_leak_checker, scripts_from_json,
1919
write_analyzer_snapshot,
2020
};
2121
use camino::Utf8Path;
@@ -167,7 +167,7 @@ pub(crate) fn analyze_and_snap(
167167
) {
168168
let mut diagnostics = Vec::new();
169169
let mut code_fixes = Vec::new();
170-
let project_layout = project_layout_with_node_manifest(input_file, &mut diagnostics);
170+
let project_layout = project_layout_for_test_file(input_file, &mut diagnostics);
171171

172172
if let Some((_, manifest)) = project_layout.find_node_manifest_for_path(input_file)
173173
&& manifest.r#type == Some(PackageType::CommonJs) &&
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
!node_modules
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/* should generate diagnostics */
2+
3+
import foo from "libs/foo";
4+
5+
import bar from "libs/bar";
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
---
2+
source: crates/biome_js_analyze/tests/spec_tests.rs
3+
expression: invalidWithPathAlias.ts
4+
---
5+
# Input
6+
```ts
7+
/* should generate diagnostics */
8+
9+
import foo from "libs/foo";
10+
11+
import bar from "libs/bar";
12+
13+
```
14+
15+
# Diagnostics
16+
```
17+
invalidWithPathAlias.ts:3:17 lint/correctness/useImportExtensions FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━
18+
19+
! Add a file extension for relative imports.
20+
21+
1 │ /* should generate diagnostics */
22+
2 │
23+
> 3 │ import foo from "libs/foo";
24+
│ ^^^^^^^^^^
25+
4 │
26+
5 │ import bar from "libs/bar";
27+
28+
i Explicit import improves compatibility with browsers and makes file resolution in tooling faster.
29+
30+
i Safe fix: Add import extension .ts.
31+
32+
1 1 │ /* should generate diagnostics */
33+
2 2 │
34+
3 │ - import·foo·from·"libs/foo";
35+
3 │ + import·foo·from·"libs/foo.ts";
36+
4 4 │
37+
5 5 │ import bar from "libs/bar";
38+
39+
40+
```
41+
42+
```
43+
invalidWithPathAlias.ts:5:17 lint/correctness/useImportExtensions FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━
44+
45+
! Add a file extension for relative imports.
46+
47+
3 │ import foo from "libs/foo";
48+
4 │
49+
> 5 │ import bar from "libs/bar";
50+
│ ^^^^^^^^^^
51+
6 │
52+
53+
i Explicit import improves compatibility with browsers and makes file resolution in tooling faster.
54+
55+
i Safe fix: Add import extension .ts.
56+
57+
5 │ import·bar·from·"libs/bar/index.ts";
58+
│ +++++++++
59+
60+
```
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"compilerOptions": {
3+
"paths": {
4+
"libs/*": ["./sub/*"]
5+
}
6+
}
7+
}

0 commit comments

Comments
 (0)