diff --git a/CHANGELOG.md b/CHANGELOG.md index 598ee551d4ac..4a4f1cc687af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ Read our [guidelines for writing a good changelog entry](https://github.com/biom - Import sorting is safe to apply now, and it will be applied when running `check --apply` instead of `check --apply-unsafe`. +- Import sorting now handles Bun imports `bun:`, absolute path imports `/`, and [Node's subpath imports `#`](https://nodejs.org/api/packages.html#subpath-imports). See [our documentation](https://biomejs.dev/analyzer/) for more details. Contributed by @Conaclos + ### CLI #### Bug fixes diff --git a/crates/biome_js_analyze/src/assists/correctness/organize_imports.rs b/crates/biome_js_analyze/src/assists/correctness/organize_imports.rs index 9bf488615ac6..6dcf6848984c 100644 --- a/crates/biome_js_analyze/src/assists/correctness/organize_imports.rs +++ b/crates/biome_js_analyze/src/assists/correctness/organize_imports.rs @@ -588,6 +588,8 @@ impl PartialEq for ImportKey { /// are listed before imports closer to the source file. #[derive(Eq, Ord, PartialEq, PartialOrd)] enum ImportCategory { + /// Anything with an explicit `bun:` prefix. + Bun, /// Anything with an explicit `node:` prefix, or one of the recognized /// Node built-ins, such `"fs"`, `"child_process"`, etc.. NodeBuiltin, @@ -601,6 +603,12 @@ enum ImportCategory { /// may (incorrectly) include source imports through custom import mappings /// as well. Library, + /// Absolute file imports `/`. + Absolute, + /// Node allows specifying an import map with name prefixed with `#`. + /// See https://nodejs.org/api/packages.html#subpath-imports + SharpImport, + /// Relative file imports `./`. Relative, /// Any unrecognized protocols are grouped here. These may include custom /// protocols such as supported by bundlers. @@ -613,11 +621,16 @@ impl From<&str> for ImportCategory { Self::Relative } else if let Some((protocol, _)) = value.split_once(':') { match protocol { + "bun" => Self::Bun, "http" | "https" => Self::Url, "node" => Self::NodeBuiltin, "npm" => Self::Npm, _ => Self::Other, } + } else if value.starts_with('#') { + Self::SharpImport + } else if value.starts_with('/') { + Self::Absolute } else if NODE_BUILTINS.binary_search(&value).is_ok() { Self::NodeBuiltin } else { diff --git a/crates/biome_js_analyze/tests/specs/correctness/organizeImports/groups.js b/crates/biome_js_analyze/tests/specs/correctness/organizeImports/groups.js index 398ed07bf293..63ff2c9ae2d8 100644 --- a/crates/biome_js_analyze/tests/specs/correctness/organizeImports/groups.js +++ b/crates/biome_js_analyze/tests/specs/correctness/organizeImports/groups.js @@ -6,3 +6,6 @@ import assert from "node:assert"; import aunt from "../aunt"; import { VERSION } from "https://deno.land/std/version.ts"; import { mock, test } from "node:test"; +import { expect } from "bun:test"; +import { internal } from "#internal"; +import { secret } from "/absolute/path"; \ No newline at end of file diff --git a/crates/biome_js_analyze/tests/specs/correctness/organizeImports/groups.js.snap b/crates/biome_js_analyze/tests/specs/correctness/organizeImports/groups.js.snap index 0687a36e3eb4..a623dd8888d6 100644 --- a/crates/biome_js_analyze/tests/specs/correctness/organizeImports/groups.js.snap +++ b/crates/biome_js_analyze/tests/specs/correctness/organizeImports/groups.js.snap @@ -12,25 +12,35 @@ import assert from "node:assert"; import aunt from "../aunt"; import { VERSION } from "https://deno.land/std/version.ts"; import { mock, test } from "node:test"; - +import { expect } from "bun:test"; +import { internal } from "#internal"; +import { secret } from "/absolute/path"; ``` # Actions ```diff -@@ -1,8 +1,8 @@ +@@ -1,11 +1,11 @@ +-import uncle from "../uncle"; +-import sibling from "./sibling"; ++import { expect } from "bun:test"; +import assert from "node:assert"; +import { mock, test } from "node:test"; -+import express from "npm:express"; -+import { VERSION } from "https://deno.land/std/version.ts"; -+import aunt from "../aunt"; - import uncle from "../uncle"; - import sibling from "./sibling"; --import express from "npm:express"; - import imageUrl from "url:./image.png"; + import express from "npm:express"; +-import imageUrl from "url:./image.png"; -import assert from "node:assert"; -import aunt from "../aunt"; --import { VERSION } from "https://deno.land/std/version.ts"; + import { VERSION } from "https://deno.land/std/version.ts"; -import { mock, test } from "node:test"; +-import { expect } from "bun:test"; ++import { secret } from "/absolute/path"; + import { internal } from "#internal"; +-import { secret } from "/absolute/path"; +\ No newline at end of file ++import aunt from "../aunt"; ++import uncle from "../uncle"; ++import sibling from "./sibling"; ++import imageUrl from "url:./image.png"; +\ No newline at end of file ``` diff --git a/website/src/content/docs/analyzer/index.mdx b/website/src/content/docs/analyzer/index.mdx index 982671cd820a..23ccf22ef74d 100644 --- a/website/src/content/docs/analyzer/index.mdx +++ b/website/src/content/docs/analyzer/index.mdx @@ -23,12 +23,15 @@ This feature is enabled by default but can be opted-in/out via configuration: Import statements are sorted by "distance". Modules that are "farther" from the user are put on the top, modules "closer" to the user are put on the bottom: -1. built-in Node.js modules that are explicitly imported using the `node:` protocol; -2. modules imported via `npm:` protocol. This is a valid syntax when writing code run by Deno, for example; -3. modules imported via URL; -4. modules imported from libraries; -5. modules imported via relative imports; -6. modules that couldn't be identified by the previous criteria; +1. modules imported via `bun:` protocol. This is applicable when writing code run by Bun; +2. built-in Node.js modules that are explicitly imported using the `node:` protocol and common Node built-ins such as `assert`; +3. modules imported via `npm:` protocol. This is applicable when writing code run by Deno; +4. modules imported via URL; +5. modules imported from libraries; +6. modules imported via absolute imports; +7. modules imported from a name prefixed by `#`. This is applicable when using [Node's subpath imports](https://nodejs.org/api/packages.html#subpath-imports); +8. modules imported via relative imports; +9. modules that couldn't be identified by the previous criteria; For example, given the following code: @@ -41,15 +44,23 @@ import assert from "node:assert"; import aunt from "../aunt"; import { VERSION } from "https://deno.land/std/version.ts"; import { mock, test } from "node:test"; +import { expect } from "bun:test"; +import { internal } from "#internal"; +import { secret } from "/absolute/path"; +import React from "react"; ``` They will be sorted like this: ```ts + import { expect } from "bun:test"; import assert from "node:assert"; import { mock, test } from "node:test"; import express from "npm:express"; import { VERSION } from "https://deno.land/std/version.ts"; + import React from "react"; + import { secret } from "/absolute/path"; + import { internal } from "#internal"; import aunt from "../aunt"; import uncle from "../uncle"; import sibling from "./sibling"; diff --git a/website/src/content/docs/internals/changelog.mdx b/website/src/content/docs/internals/changelog.mdx index 73bfcf461dfd..8125d5f2f32e 100644 --- a/website/src/content/docs/internals/changelog.mdx +++ b/website/src/content/docs/internals/changelog.mdx @@ -22,6 +22,8 @@ Read our [guidelines for writing a good changelog entry](https://github.com/biom - Import sorting is safe to apply now, and it will be applied when running `check --apply` instead of `check --apply-unsafe`. +- Import sorting now handles Bun imports `bun:`, absolute path imports `/`, and [Node's subpath imports `#`](https://nodejs.org/api/packages.html#subpath-imports). See [our documentation](https://biomejs.dev/analyzer/) for more details. Contributed by @Conaclos + ### CLI #### Bug fixes