Skip to content

Commit

Permalink
refactor: improve import namespace handling
Browse files Browse the repository at this point in the history
  • Loading branch information
Conaclos committed May 10, 2024
1 parent 7245146 commit 85b0d4e
Show file tree
Hide file tree
Showing 12 changed files with 339 additions and 29 deletions.
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,31 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b

#### Bug fixes

- [noUndeclaredVariables](https://biomejs.dev/linter/rules/no-undeclared-variables/) and [noUnusedImports](https://biomejs.dev/linter/rules/no-unused-imports) now correctly handle import namespaces ([#2796](https://github.com/biomejs/biome/issues/2796)).

Previously, Biome bound unqualified type to import namespaces.
Import namespaces can only be used as qualified names in a type (ambient) context.

```ts
// Unused import
import * as Ns1 from "";
// This doesn't reference the import namespace `Ns1`
type T1 = Ns1; // Undeclared variable `Ns1`

// Unused import
import type * as Ns2 from "";
// This doesn't reference the import namespace `Ns2`
type T2 = Ns2; // Undeclared variable `Ns2`

import type * as Ns3 from "";
// This references the import namespace because it is a qualified name.
type T3 = Ns3.Inner;
// This also references the import namespace.
export type { Ns3 }
```

Contributed by @Conaclos

- `useJsxKeyInIterable` now handles more cases involving fragments. See the snippets below. Contributed by @dyc3
```jsx
// valid
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import * as Ns1 from "";
export type T1 = Ns1; // This doesn't reference the import namespace `Ns1`

import type * as Ns2 from "";
export type T2 = Ns2; // This doesn't reference the import namespace `Ns1`
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
source: crates/biome_js_analyze/tests/spec_tests.rs
expression: invalidNamesapceReference.ts
---
# Input
```ts
import * as Ns1 from "";
export type T1 = Ns1; // This doesn't reference the import namespace `Ns1`

import type * as Ns2 from "";
export type T2 = Ns2; // This doesn't reference the import namespace `Ns1`
```

# Diagnostics
```
invalidNamesapceReference.ts:2:18 lint/correctness/noUndeclaredVariables ━━━━━━━━━━━━━━━━━━━━━━━━━━━
! The Ns1 variable is undeclared
1 │ import * as Ns1 from "";
> 2 │ export type T1 = Ns1; // This doesn't reference the import namespace `Ns1`
│ ^^^
3 │
4 │ import type * as Ns2 from "";
```

```
invalidNamesapceReference.ts:5:18 lint/correctness/noUndeclaredVariables ━━━━━━━━━━━━━━━━━━━━━━━━━━━
! The Ns2 variable is undeclared
4 │ import type * as Ns2 from "";
> 5 │ export type T2 = Ns2; // This doesn't reference the import namespace `Ns1`
│ ^^^
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import * as Ns1 from ""
export type T1 = Ns1;

import type * as Ns2 from ""
export type T2 = Ns2;
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
source: crates/biome_js_analyze/tests/spec_tests.rs
expression: invalid-import-namespace.ts
---
# Input
```ts
import * as Ns1 from ""
export type T1 = Ns1;

import type * as Ns2 from ""
export type T2 = Ns2;
```

# Diagnostics
```
invalid-import-namespace.ts:1:13 lint/correctness/noUnusedImports FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━
! This import is unused.
> 1 │ import * as Ns1 from ""
│ ^^^
2 │ export type T1 = Ns1;
3 │
i Unused imports might be the result of an incomplete refactoring.
i Safe fix: Remove the unused import.
1 │ import·*·as·Ns1·from·""
│ -----------------------
```

```
invalid-import-namespace.ts:4:18 lint/correctness/noUnusedImports FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━
! This import is unused.
2 │ export type T1 = Ns1;
3 │
> 4 │ import type * as Ns2 from ""
│ ^^^
5 │ export type T2 = Ns2;
i Unused imports might be the result of an incomplete refactoring.
i Safe fix: Remove the unused import.
1 1 │ import * as Ns1 from ""
2 2 │ export type T1 = Ns1;
3 │ -
4 │ - import·type·*·as·Ns2·from·""
5 3 │ export type T2 = Ns2;
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
namespace Ns {}
export type { Ns }
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
source: crates/biome_js_analyze/tests/spec_tests.rs
expression: validNamesapceExportType.ts
---
# Input
```ts
namespace Ns {}
export type { Ns }
```
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,7 @@ export type { f2, Class2, typeNs };
declare class AmbientClass {}
declare enum AmbientEnum {}
declare class AmbientFunction {}
export { AmbientClass, AmbientEnum, AmbientFunction }
export { AmbientClass, AmbientEnum, AmbientFunction }

import type * as Ns from ""
export { Ns }
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,26 @@ declare class AmbientClass {}
declare enum AmbientEnum {}
declare class AmbientFunction {}
export { AmbientClass, AmbientEnum, AmbientFunction }

import type * as Ns from ""
export { Ns }
```

# Diagnostics
```
valid.ts:44:8 lint/style/useExportType FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
! All exports are only types and should thus use export type.
43 │ import type * as Ns from ""
> 44 │ export { Ns }
│ ^^^^^^
i Using export type allows transpilers to safely drop exports of types without looking for their definition.
i Safe fix: Use a grouped export type.
44 │ export·type·{·Ns·}
│ +++++
```
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,10 @@ function f<T, T>() {}

function g<T>() {
type T = number;
}
}

import * as Ns1 from ""
namespace Ns1 {}

import type * as Ns2 from ""
namespace Ns2 {}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ function f<T, T>() {}
function g<T>() {
type T = number;
}

import * as Ns1 from ""
namespace Ns1 {}

import type * as Ns2 from ""
namespace Ns2 {}

```

# Diagnostics
Expand Down Expand Up @@ -76,6 +83,7 @@ invalid.ts:11:10 lint/suspicious/noRedeclare ━━━━━━━━━━━
> 11 │ type T = number;
│ ^
12 │ }
13 │
i 'T' is defined here:
Expand All @@ -88,3 +96,48 @@ invalid.ts:11:10 lint/suspicious/noRedeclare ━━━━━━━━━━━
```

```
invalid.ts:15:11 lint/suspicious/noRedeclare ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
! Shouldn't redeclare 'Ns1'. Consider to delete it or rename it.
14 │ import * as Ns1 from ""
> 15 │ namespace Ns1 {}
│ ^^^
16 │
17 │ import type * as Ns2 from ""
i 'Ns1' is defined here:
12 │ }
13 │
> 14 │ import * as Ns1 from ""
│ ^^^
15 │ namespace Ns1 {}
16 │
```

```
invalid.ts:18:11 lint/suspicious/noRedeclare ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
! Shouldn't redeclare 'Ns2'. Consider to delete it or rename it.
17 │ import type * as Ns2 from ""
> 18 │ namespace Ns2 {}
│ ^^^
19 │
i 'Ns2' is defined here:
15 │ namespace Ns1 {}
16 │
> 17 │ import type * as Ns2 from ""
│ ^^^
18 │ namespace Ns2 {}
19 │
```

0 comments on commit 85b0d4e

Please sign in to comment.