Skip to content

Commit b92d845

Browse files
committed
docs: add action docs
1 parent 9004353 commit b92d845

File tree

4 files changed

+241
-98
lines changed

4 files changed

+241
-98
lines changed

crates/biome_configuration/src/analyzer/assist/actions.rs

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/biome_js_analyze/src/assist/source/organize_imports.rs

+238-95
Original file line numberDiff line numberDiff line change
@@ -22,131 +22,274 @@ pub mod specifiers_attributes;
2222
mod util;
2323

2424
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+
/// }
58112
/// ```
59113
///
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";
76119
/// ```
77120
///
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.
79124
///
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 `@/`.
81127
///
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:
128+
/// ```json,options
129+
/// {
130+
/// "options": {
131+
/// "groups": [
132+
/// ["@any/lib", "@any/lib/**", "!@any/lib/special", "!@any/lib/special/**"],
133+
/// "@/**"
134+
/// ]
135+
/// }
136+
/// }
137+
/// ```
83138
///
84-
/// ```js
85-
/// import "../styles/reset.css";
86-
/// import "../styles/layout.css";
87-
/// 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";
88144
/// ```
89145
///
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.
156+
///
157+
/// ```json,options
158+
/// {
159+
/// "options": {
160+
/// "groups": [
161+
/// ":NODE:",
162+
/// ":BLANK-LINE:"
163+
/// ["@any/lib", "@any/lib/**", "!@any/lib/special", "!@any/lib/special/**"],
164+
/// "@/**"
165+
/// ]
166+
/// }
167+
/// }
168+
/// ```
91169
///
92-
/// ```js
93-
/// import "../polyfills/array/flatMap";
94-
/// 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";
95176
/// ```
96177
///
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+
/// ```
98189
///
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:
100193
///
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
102199
///
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.
107204
///
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";
110213
/// ```
111214
///
112-
/// ```js
113-
/// // group 1, the polyfill/shim
114-
/// import "../polyfills/array/flatMap";
215+
/// ## Last resort sorting
115216
///
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" } ;
118240
/// ```
119241
///
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.
121250
///
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";
123258
///
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";
126265
/// ```
127266
///
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";
129274
///
130-
/// For example, the following imports form 4 import groups.
275+
/// export { A, b, a, B, c10, c9 } from "a";
131276
///
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" };
139278
/// ```
140279
///
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:
142286
///
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
149-
/// import React from "react"; // Import group 4
287+
/// ```json title="settings.json"
288+
/// {
289+
/// "editor.codeActionsOnSave":{
290+
/// "source.organizeImports.biome": "explicit"
291+
/// }
292+
/// }
150293
/// ```
151294
pub OrganizeImports {
152295
version: "1.0.0",

packages/@biomejs/backend-jsonrpc/src/workspace.ts

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@biomejs/biome/configuration_schema.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)