You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardexpand all lines: crates/biome_js_analyze/src/assist/source/organize_imports.rs
+238-95
Original file line number
Diff line number
Diff line change
@@ -22,131 +22,274 @@ pub mod specifiers_attributes;
22
22
mod util;
23
23
24
24
declare_source_rule!{
25
-
/// Provides a whole-source code action to sort the imports in the file using import groups and natural ordering.
26
-
///
27
-
/// ## How imports are sorted
28
-
///
29
-
/// 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:
30
-
///
31
-
/// 1. modules imported via `bun:` protocol. This is applicable when writing code run by Bun;
32
-
/// 1. built-in Node.js modules that are explicitly imported using the `node:` protocol and common Node built-ins such as `assert`;
33
-
/// 1. modules imported via `npm:` protocol. This is applicable when writing code run by Deno;
34
-
/// 1. modules that contain the protocol `:`. These are usually considered "virtual modules", modules that are injected by your working environment, e.g. `vite`;
35
-
/// 1. modules imported via URL;
36
-
/// 1. modules imported from libraries;
37
-
/// 1. modules imported via absolute imports;
38
-
/// 1. modules imported from a name prefixed by `#`. This is applicable when using [Node's subpath imports](https://nodejs.org/api/packages.html#subpath-imports);
39
-
/// 1. modules imported via relative imports;
40
-
/// 1. modules that couldn't be identified by the previous criteria;
41
-
///
42
-
/// For example, given the following code:
43
-
///
44
-
/// ```ts
45
-
/// import uncle from "../uncle";
46
-
/// import sibling from "./sibling";
47
-
/// import express from "npm:express";
48
-
/// import imageUrl from "url:./image.png";
49
-
/// import { sortBy } from "virtual:utils";
50
-
/// import assert from "node:assert";
51
-
/// import aunt from "../aunt";
52
-
/// import { VERSION } from "https://deno.land/std/version.ts";
53
-
/// import { mock, test } from "node:test";
54
-
/// import { expect } from "bun:test";
55
-
/// import { internal } from "#internal";
56
-
/// import { secret } from "/absolute/path";
57
-
/// import React from "react";
25
+
/// Provides a code action to sort the imports and the exports in the file using a built-in order or a custom order.
26
+
///
27
+
/// Imports and exports are first separated into chunks, before being sorted.
28
+
/// Imports or exports of a chunk are then grouped according to the user-defined groups.
29
+
/// Within a group, imports are sorted using a built-in order that depends on the import/export type, if the import/export has attributes, and its source.
30
+
/// The next sections defines all these aspects.
31
+
///
32
+
/// ## Chunk of import and chunks of exports
33
+
///
34
+
/// A **chunk** is a sequence of adjacent imports or exports.
35
+
/// A chunk contains only imports or only exports, not both at the same time.
36
+
/// The following example contains two chunks.
37
+
/// The first chunk consists of the two first imports and the second chunks consists of the two exports.
38
+
/// The first appearing `export` ends the first chunk and starts a new one.
39
+
///
40
+
/// ```js,ignore
41
+
/// import A from "a";
42
+
/// import * as B from "b";
43
+
/// export { C } from "c";
44
+
/// export { D } from "d";
45
+
/// ```
46
+
///
47
+
/// Chunks also ends as soon as a statement or a side-effect import (also called bare import) is encountered.
48
+
/// The following example contains three chunks.
49
+
///
50
+
/// ```js,ignore
51
+
/// import A from "a";
52
+
/// import "x";
53
+
/// import * as B from "b";
54
+
/// function f() {}
55
+
/// import * as C from "c";
56
+
/// ```
57
+
///
58
+
/// Finally, a last way of intentionally start a new chunk is to use a detached comment.
59
+
/// A **detached comment** is a comment separated of an import or export with a blank line.
60
+
/// Comments that are not separated of an import or export by a blank line are **attached comments**.
61
+
/// The following example contains a detached comment that split the imports into two chunks.
62
+
///
63
+
/// ```js,ignore
64
+
/// // Attached comment 1
65
+
/// import A from "a";
66
+
/// // Attached comment 2
67
+
/// import * as B from "b";
68
+
///
69
+
/// // Detached comment
70
+
///
71
+
/// import * as C from "c";
72
+
/// ```
73
+
///
74
+
/// The import and export organizer ensures that chunks are separated from each other with a blank lines.
75
+
/// Only side-effect imports adjacent to a chunk are not separated by a blank line.
76
+
///
77
+
/// Imports or exports of a chunk are sorted together.
78
+
/// Import and export sorting uses several layer of order.
79
+
///
80
+
/// ## Import and export groups
81
+
///
82
+
/// Imports or exports of a chunk are divided into groups.
83
+
/// By default, it exists a single group: the implicit group.
84
+
///
85
+
/// You can define extra groups using the `groups` option.
86
+
/// The organizer provides predefined groups and allows you to create new groups based on a glob or a list of globs.
87
+
/// The predefined groups are:
88
+
///
89
+
/// - `:NODE:`: all imports and exports with a source starting by the protocol `node:` or that corresponds to a builtin Node,js module such as `path`
90
+
/// - `:BUN:`: all imports and exports with a source starting by the protocol `bun:`
91
+
///
92
+
/// Given the following configuration and code, the import organizer propose to reorder the imports to place the Node.js imports first.
93
+
///
94
+
/// ```json,full_options
95
+
/// {
96
+
/// "assist": {
97
+
/// "actions": {
98
+
/// "source": {
99
+
/// "organizeImports": {
100
+
/// "level": "on",
101
+
/// "options": {
102
+
/// "groups": [
103
+
/// ":NODE:",
104
+
/// ":BUN:"
105
+
/// ]
106
+
/// }
107
+
/// }
108
+
/// }
109
+
/// }
110
+
/// }
111
+
/// }
58
112
/// ```
59
113
///
60
-
/// They will be sorted like this:
61
-
///
62
-
/// ```ts
63
-
/// import { expect } from "bun:test";
64
-
/// import assert from "node:assert";
65
-
/// import { mock, test } from "node:test";
66
-
/// import express from "npm:express";
67
-
/// import { sortBy } from "virtual:utils";
68
-
/// import { VERSION } from "https://deno.land/std/version.ts";
69
-
/// import React from "react";
70
-
/// import { secret } from "/absolute/path";
71
-
/// import { internal } from "#internal";
72
-
/// import aunt from "../aunt";
73
-
/// import uncle from "../uncle";
74
-
/// import sibling from "./sibling";
75
-
/// import imageUrl from "url:./image.png";
114
+
/// ```js,expect_diagnostic,use_options
115
+
/// import { Database } from "bun:sqlite";
116
+
/// import lib from "@any/lib";
117
+
/// import test from "node:test";
118
+
/// import path from "path";
76
119
/// ```
77
120
///
78
-
/// You can apply the sorting in two ways: via [CLI](#import-sorting-via-cli) or [VSCode extension](#import-sorting-via-vscode-extension).
121
+
/// Note that, `import lib from "@any/lib"` doesn't match any explicitly set groups.
122
+
/// Imports and exports that don't match any user-provided groups, always match the implicit group that appears after all other groups.
123
+
/// Thus the import is placed at the end.
79
124
///
80
-
/// ## Grouped imports
125
+
/// Groups can also be created using a glob or a list of globs.
126
+
/// In the following example, we create two groups: one that gathers imports/exports starting with `@any/lib` except `@any/lib/speciaal` and the other that gathers imports/exports starting with `@/`.
81
127
///
82
-
/// It's widespread to have import statements in a certain order, primarily when you work on a frontend project, and you import CSS files:
/// import { Grid } from "../components/Grid.jsx";
139
+
/// ```js,expect_diagnostic,use_options
140
+
/// import lib from "@any/lib";
141
+
/// import aliased from "@/alias";
142
+
/// import path from "@any/lib/special";
143
+
/// import test from "@any/lib/path";
88
144
/// ```
89
145
///
90
-
/// Another common case is import polyfills or shim files, that needs to stay at the top file:
146
+
/// Note that `@any/lib` matches `@any/lib` but not `@any/lib/**`.
147
+
/// Conversely, `@any/lib/subpath` matches `@any/lib/**`, but not `@any/lib`.
148
+
/// Thus you have to specify the two patterns if you want to accept all imports/exports that starts with `@any/lib`.
149
+
/// The prefix `!` indicates an exception.
150
+
/// For more details on the supported glob patterns, see the dedicated section.
151
+
///
152
+
/// For now, it is not possible to mix predefined group and globs inside a list globs.
153
+
/// This is a limitation that we are working to remove.
154
+
///
155
+
/// The import organizer also allows you to indicate you want to separate two groups with a blank line using the predefined string `:BLANK-LINE:` like demonstrated in the following example.
/// import { functionThatUsesFlatMap } from "./utils.js";
170
+
/// ```js,expect_diagnostic,use_options
171
+
/// import path from "node:path";
172
+
/// import lib from "@any/lib";
173
+
/// import test from "@any/lib/path";
174
+
/// import path from "@any/lib/special";
175
+
/// import aliased from "@/alias";
95
176
/// ```
96
177
///
97
-
/// In these cases, Biome will sort all these three imports, and it might happen that the order will **break** your application.
178
+
/// Once divided in chunk and groups, imports and exports are sorted according to their source.
179
+
///
180
+
/// ## Import and export sources
181
+
///
182
+
/// The source of an import or export is the string coming just after the `from` keyword.
183
+
/// In the following example the `import` has `a` as source, while the `export` has `c` as source
184
+
///
185
+
/// ```js,ignore
186
+
/// import A from "a";
187
+
/// export { C } from "c";
188
+
/// ```
98
189
///
99
-
/// To avoid this, create a "group" of imports. You create a "group" by adding a **new line** to separate the groups.
190
+
/// Import and export sources are ordered by "distance".
191
+
/// Sources that are "farther" from the user are put on the top, sources "closer" to the user are put on the bottom.
192
+
/// The following list provides an overview of this order:
100
193
///
101
-
/// By doing so, Biome will limit the sorting only to the import statements that belong to the same group:
194
+
/// 1. URLs such as `https://example.org`
195
+
/// 2. Packages with a protocol such as `node:`, `bun:`, `jsr:`, or `npm:`
196
+
/// 3. Packages such as `mylib` and `@my/lib`
197
+
/// 4. Aliases such as `@/`, `#`, `~`, or `%`
198
+
/// 5. Absolute and relative paths such as
102
199
///
103
-
/// ```js
104
-
/// // group 1, only these two files will be sorted
105
-
/// import "../styles/reset.css";
106
-
/// import "../styles/layout.css";
200
+
/// Two imports/exports with the same category are sorted using a [natural sort order](https://en.wikipedia.org/wiki/Natural_sort_order) adapted to URLs, packages, and paths.
201
+
/// Notably, the order ensures that `a < A < b < B`.
202
+
/// The order takes also numbers into account, e.g. `a9 < a10`.
203
+
/// Let's just takes the following example to demonstrate this order.
107
204
///
108
-
/// // group 2, only this one is sorted
109
-
/// import { Grid } from "../components/Grid.jsx";
205
+
/// ```js,expect_diagnostic
206
+
/// import path from "node:path";
207
+
/// import example from "https://example.org";
208
+
/// import B from "./b.js";
209
+
/// import A from "../a.js";
210
+
/// import alias from "@/alias";
211
+
/// import myLib from "@my/lib";
212
+
/// import lib from "lib";
110
213
/// ```
111
214
///
112
-
/// ```js
113
-
/// // group 1, the polyfill/shim
114
-
/// import "../polyfills/array/flatMap";
215
+
/// ## Last resort sorting
115
216
///
116
-
/// // group 2, the files that require the polyfill/shim
117
-
/// import { functionThatUsesFlatMap } from "./utils.js";
217
+
/// If two imports/exports share the same source and are in the same chunk, then they are ordered according to their kind as follows:
218
+
///
219
+
/// - Default type import
220
+
/// - Default import
221
+
/// - Combined default and namespace import
222
+
/// - Combined default and named import
223
+
/// - Namespace type import / Namespace type export
224
+
/// - Namespace import / Namespace export
225
+
/// - Named type import / Named type export
226
+
/// - Named import / Named export
227
+
///
228
+
/// Also, import and exports with attributes are always placed first.
229
+
///
230
+
/// ```js,expect_diagnostic
231
+
/// import * as namespaceImport from "same-source";
232
+
/// import type * as namespaceTypeImport from "same-source";
233
+
/// import { namedImport } from "same-source";
234
+
/// import type { namedTypeImport } from "same-source";
235
+
/// import defaultNamespaceCombined, * as namespaceCombined from "same-source";
236
+
/// import defaultNamedCombined, { namedCombined } from "same-source";
237
+
/// import defaultImport from "same-source";
238
+
/// import type defaultTypeImport from "same-source";
239
+
/// import { importWithAttribute } from "same-source" with { "attribute": "value" } ;
118
240
/// ```
119
241
///
120
-
/// ## Side effect imports
242
+
/// ## Comment handling
243
+
///
244
+
/// When sorting imports and exports, attached comments are moved with their import or export,
245
+
/// and detached comments (comments followed by a blank line) are left where they are.
246
+
///
247
+
/// Note that there is an exception at the rule.
248
+
/// If a comment appears at the top of the file, it is considered as detached even if no blank lien follows.
249
+
/// This ensures that copyright notice and file header comment stay at the top of the file.
121
250
///
122
-
/// [Side effect imports](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#forms_of_import_declarations) are import statements that usually don't import any name:
251
+
/// ```js,expect_diagnostic
252
+
/// // Copyright notice and file header comment
253
+
/// import F from "f";
254
+
/// // Attached comment for `e`
255
+
/// import E from "e";
256
+
/// // Attached comment for `d`
257
+
/// import D from "d";
123
258
///
124
-
/// ```js
125
-
/// import "./global.js"
259
+
/// // Detached comment (new chunk)
260
+
///
261
+
/// // Attached comment for `b`
262
+
/// import B from "b";
263
+
/// // Attached comment for `a`
264
+
/// import A from "a";
126
265
/// ```
127
266
///
128
-
/// Since it is difficult to determine which side effects a module triggers, the import sorter assumes that each side effect import forms its own import group.
267
+
/// ## Named imports/exports and attributes sorting
268
+
///
269
+
/// The import and export organizer also sort named imports, named exports, as well as attributes.
270
+
/// It uses a natural sort order.
271
+
///
272
+
/// ```js,expect_diagnostic
273
+
/// import { A, b, a, B, c10, c9 } from "a";
129
274
///
130
-
/// For example, the following imports form 4 import groups.
275
+
/// export { A, b, a, B, c10, c9 } from "a";
131
276
///
132
-
/// ```js
133
-
/// import sibling from "./sibling"; // Import group 1
134
-
/// import { internal } from "#internal"; // Import group 1
135
-
/// import "z"; // Import group 2
136
-
/// import "a"; // Import group 3
137
-
/// import React from "react"; // Import group 4
138
-
/// import assert from "node:assert"; // Import group 4
277
+
/// import special from "special" with { "type": "ty", "metadata": "data" };
139
278
/// ```
140
279
///
141
-
/// Each group is independently sorted as follows:
280
+
/// ## Import sorting via VSCode extension
281
+
///
282
+
/// Any LSP-compatible editor supports imports sorting through the "Organize Imports" code action.
283
+
/// By default, this action can be run using the <kbd title="Shift">⇧</kbd>+<kbd>Alt</kbd>+<kbd>O</kbd> keyboard shortcut, or is accessible through the _Command Palette_ (<kbd>Ctrl</kbd>/<kbd title="Cmd">⌘</kbd>+<kbd title="Shift">⇧</kbd>+<kbd>P</kbd>) by selecting _Organize Imports_.
284
+
///
285
+
/// You can add the following to your VSCode extension configuration if you want the action to run automatically on save instead of calling it manually:
142
286
///
143
-
/// ```js
144
-
/// import { internal } from "#internal"; // Import group 1
145
-
/// import sibling from "./sibling"; // Import group 1
146
-
/// import "z"; // Import group 2
147
-
/// import "a"; // Import group 3
148
-
/// import assert from "node:assert"; // Import group 4
0 commit comments