prettier-plugin-sort 0.1.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,20 +2,20 @@
2
2
 
3
3
  A [Prettier](https://prettier.io/) plugin focused on sorting.
4
4
 
5
- - Sort `import` declarations in JS / TS files
6
- - Sort named specifiers inside `export { … }`
7
- - Sort top-level keys, string arrays, and dependency maps in `package.json`
8
- - Zero runtime dependencies
5
+ - Sort top-level `import` and `export { ... }` in JavaScript, TypeScript, and Flow
6
+ - Sort fields in `package.json`
9
7
 
10
8
  Read this in other languages: English | [中文](./README.zh.md)
11
9
 
12
10
  ## Install
13
11
 
12
+ Requires Prettier 3.9 or newer.
13
+
14
14
  ```shell
15
15
  npm i -D prettier prettier-plugin-sort
16
16
  ```
17
17
 
18
- Enable it in your Prettier config:
18
+ Add the plugin to your Prettier config:
19
19
 
20
20
  ```json
21
21
  {
@@ -23,74 +23,92 @@ Enable it in your Prettier config:
23
23
  }
24
24
  ```
25
25
 
26
- Then run Prettier as usual, for example `npx prettier --write .`.
26
+ ## Scope
27
+
28
+ ES module sorting supports these Prettier parsers:
29
+
30
+ - `babel`
31
+ - `babel-flow`
32
+ - `babel-ts`
33
+ - `typescript`
34
+ - `flow`
35
+ - `acorn`
36
+ - `espree`
37
+ - `meriyah`
38
+
39
+ The same sorting also applies to **embedded** JavaScript and TypeScript in Vue or Markdown.
27
40
 
28
- ## What gets sorted
41
+ The plugin only handles ES module syntax. CommonJS `require()` and `module.exports`, along with TypeScript `export =`, remain unchanged.
29
42
 
30
- ### Imports
43
+ `package.json` sorting is enabled for these Prettier parsers:
44
+
45
+ - `json`
46
+ - `json-stringify`
47
+
48
+ Sorting is enabled only when the file is named `package.json`.
49
+
50
+ ## Options
31
51
 
32
- #### Grouping and sorting
52
+ | Option | Type | Default | Description |
53
+ | ------------------------ | ----------------- | ------------------------------------------------------- | ------------------------------------------------------------------------ |
54
+ | `esmImportSort` | `boolean` | `true` | Group and sort top-level static imports and the specifiers inside braces |
55
+ | `esmImportGroups` | `ImportGroup[]` | `["builtin", "external", "parent", "sibling", "index"]` | Set the order of import groups |
56
+ | `esmImportSeparation` | `boolean` | `true` | Add blank lines between groups and above and below side-effect imports |
57
+ | `esmImportTypeStyle` | `TypeImportStyle` | `"separate"` | Control the form and order of type-only imports |
58
+ | `esmImportMerge` | `boolean` | `true` | Safely merge imports from the same module |
59
+ | `esmExportSpecifierSort` | `boolean` | `true` | Sort specifiers inside `export { ... }` |
60
+ | `packageSort` | `boolean` | `true` | Sort fields in `package.json` |
33
61
 
34
- With the default config, imports are grouped and ordered like this.
62
+ ## Import sorting
35
63
 
36
- Before:
64
+ The plugin groups and sorts top-level static imports. It also sorts the specifiers inside braces. Import specifiers that use `as` are sorted by their local names.
65
+
66
+ The plugin leaves dynamic `import()` calls and import-like text in strings or comments unchanged. Set `esmImportSort` to `false` to disable all import sorting.
67
+
68
+ The plugin collects static imports separated by other top-level statements at the first import position, then sorts them.
69
+
70
+ Before sorting:
37
71
 
38
72
  <!-- prettier-ignore -->
39
73
  ```typescript
40
- import App from './App.tsx';
74
+ import App from './App';
41
75
  import fs from 'node:fs';
42
- import lodash from 'lodash';
43
- import path from 'node:path';
44
76
  import react from 'react';
45
77
  ```
46
78
 
47
- After:
79
+ After sorting:
48
80
 
49
81
  ```typescript
50
82
  import fs from 'node:fs';
51
- import path from 'node:path';
52
83
 
53
- import lodash from 'lodash';
54
84
  import react from 'react';
55
85
 
56
- import App from './App.tsx';
86
+ import App from './App';
57
87
  ```
58
88
 
59
- Each import belongs to a group. For example, `node:fs` is a `builtin` (covers Node.js and Bun **runtime** built-ins), while `react` and `lodash` are `external` npm packages. The plugin first classifies each import by group, then sorts alphabetically within each group.
89
+ ### Grouping
60
90
 
61
- Grouping and ordering follow the conventions of [eslint-plugin-import](https://github.com/import-js/eslint-plugin-import)'s [import/order](https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/order.md) rule.
91
+ `esmImportGroups` accepts these values:
62
92
 
63
- The default import config is:
93
+ | Group | Matches |
94
+ | ---------- | -------------------------------------------------------------- |
95
+ | `builtin` | Modules beginning with `node:` or `bun:`, plus `bun` |
96
+ | `external` | Packages and module paths not matched by any other group |
97
+ | `internal` | Module paths beginning with `/`, `~`, `@/`, or `#` |
98
+ | `parent` | Parent-relative paths such as `../utils` |
99
+ | `sibling` | Sibling paths such as `./Button` |
100
+ | `index` | `.`, `./`, `./index`, and `./index` with an optional extension |
64
101
 
65
- ```json
66
- {
67
- "plugins": ["prettier-plugin-sort"],
68
- "importOrderGroups": ["builtin", "external", "parent", "sibling", "index"],
69
- "importOrderSeparation": true,
70
- "importOrderTypeImports": "separate",
71
- "importOrderMergeDuplicates": true
72
- }
73
- ```
74
-
75
- Group matchers:
102
+ Modern Node.js code should reference built-ins explicitly with [`node:` URLs](https://nodejs.org/api/esm.html#node-imports). The plugin also classifies only `node:`-prefixed Node.js built-ins as `builtin`. Module specifiers without the prefix, such as `fs` and `path`, belong to `external`.
76
103
 
77
- | Group | Matches | Examples |
78
- | ---------- | ----------------------------------------------------------- | ---------------------------------- |
79
- | `builtin` | `node:*`, `bun:*`, `bun`, and unprefixed Node built-ins | `node:fs`, `path`, `bun` |
80
- | `external` | npm packages, and anything that doesn't match another group | `react`, `@scope/pkg` |
81
- | `internal` | Project absolute paths and aliases | `/utils`, `~/app`, `@/shared` |
82
- | `parent` | Parent-relative paths | `../Button` |
83
- | `sibling` | Sibling paths (excluding index) | `./Icon` |
84
- | `index` | Current directory index | `.`, `./`, `./index`, `./index.ts` |
104
+ The plugin removes duplicate entries and appends omitted default groups in their default order. The default configuration does not include `internal`, so it sorts last unless explicitly added.
85
105
 
86
- > **Note on `internal` detection:** the plugin currently uses hardcoded specifier prefixes (`/`, `~`, `@/`) and does not read `tsconfig paths` or any bundler config. An `importOrderInternalPatterns` option for custom regex matching may be added in a future release.
87
-
88
- Reorder or drop groups through `importOrderGroups`. For example, adding `internal` explicitly:
106
+ Projects that use one of these path aliases can add `internal`:
89
107
 
90
108
  ```json
91
109
  {
92
110
  "plugins": ["prettier-plugin-sort"],
93
- "importOrderGroups": [
111
+ "esmImportGroups": [
94
112
  "builtin",
95
113
  "external",
96
114
  "internal",
@@ -101,231 +119,92 @@ Reorder or drop groups through `importOrderGroups`. For example, adding `interna
101
119
  }
102
120
  ```
103
121
 
104
- Before:
105
-
106
- <!-- prettier-ignore -->
107
- ```typescript
108
- import App from './App.tsx';
109
- import react from 'react';
110
- import shared from '@/shared';
111
- ```
112
-
113
- After:
114
-
115
- <!-- prettier-ignore -->
116
- ```typescript
117
- import react from 'react';
118
-
119
- import shared from '@/shared';
120
-
121
- import App from './App.tsx';
122
- ```
123
-
124
- Set `importOrderSeparation` to `false` if you don't want blank lines between groups.
125
-
126
- #### Type imports
122
+ The plugin does not read `compilerOptions.paths` or resolve aliases from bundler configuration.
127
123
 
128
- By default the plugin splits `type` imports into their own statement.
124
+ Set `esmImportSeparation` to `false` to remove blank lines between groups and above and below side-effect imports.
129
125
 
130
- Before:
131
-
132
- <!-- prettier-ignore -->
133
- ```typescript
134
- import { useState, type FC } from 'react';
135
- ```
136
-
137
- After:
138
-
139
- <!-- prettier-ignore -->
140
- ```typescript
141
- import type { FC } from 'react';
142
- import { useState } from 'react';
143
- ```
126
+ ### Type-only import style
144
127
 
145
- The shape of `importOrderTypeImports` mirrors conventions in the ESLint ecosystem, especially the `fixStyle` option of [@typescript-eslint/consistent-type-imports](https://typescript-eslint.io/rules/consistent-type-imports).
128
+ `esmImportTypeStyle` controls how type-only imports are written and accepts four values:
146
129
 
147
- Using `import { c, type B, a } from 'mod';` as an example:
148
-
149
- | Mode | Result |
130
+ | Value | Input: `import { c, type B, a } from 'mod'` |
150
131
  | -------------- | ---------------------------------------------------------------- |
151
132
  | `separate` | `import type { B } from 'mod';`<br>`import { a, c } from 'mod';` |
152
133
  | `inline-first` | `import { type B, a, c } from 'mod';` |
153
134
  | `inline-last` | `import { a, c, type B } from 'mod';` |
154
135
  | `mixed` | `import { a, type B, c } from 'mod';` |
155
136
 
156
- `separate`, `inline-first`, and `inline-last` sort type and value specifiers independently within their own group. `mixed` sorts all specifiers together alphabetically (case-insensitive), keeping the `type` keyword inline where needed.
157
-
158
- #### Merging same-source imports
159
-
160
- By default, multiple `import` statements from the same source are merged into one.
161
-
162
- Before:
163
-
164
- <!-- prettier-ignore -->
165
- ```typescript
166
- import { useState } from 'react';
167
- import { useEffect } from 'react';
168
- ```
137
+ `separate` is the default. Type-only default and namespace imports keep their original form.
169
138
 
170
- After:
139
+ ### Merging and sorting boundaries
171
140
 
172
- ```typescript
173
- import { useEffect, useState } from 'react';
174
- ```
141
+ With `esmImportMerge` enabled, imports from the same module are merged when it is safe to do so.
175
142
 
176
- `importOrderMergeDuplicates` only handles the merge step itself. The arrangement inside the braces is entirely controlled by `importOrderTypeImports`. For instance, merging `import { useState } from 'react';` and `import { type FC, useEffect } from 'react';` produces:
143
+ The plugin does not merge imports with different import attributes, comments that cannot move safely, or conflicting default or namespace bindings. Side-effect imports also remain separate.
177
144
 
178
- - `separate` (default): the merged statement is split back into two at the type-import stage, so you end up with an independent `import type` statement
179
- - `inline-first`: `import { type FC, useEffect, useState } from 'react';`
180
- - `inline-last`: `import { useEffect, useState, type FC } from 'react';`
181
- - `mixed`: `import { type FC, useEffect, useState } from 'react';`
145
+ Sorting never crosses these boundaries:
182
146
 
183
- Set `importOrderMergeDuplicates` to `false` if you want to keep the original separate statements. Side-effect imports (`import 'mod';`) are never merged because their order has runtime semantics.
147
+ - Side-effect import order can affect CSS cascading or polyfill loading, so these imports are not sorted. They keep their relative positions and separate the sorting segments around them.
148
+ - A declaration marked with `prettier-ignore` stays unchanged. Imports before and after it are sorted separately.
149
+ - A standalone comment separates the imports before and after it. A comment attached to an import moves with that declaration.
150
+ - The plugin preserves shebangs, Prettier file pragmas, and position-sensitive ESLint directives.
151
+ - Specialized declarations such as `import source`, `import defer`, and Flow `import typeof` may be reordered as a whole. The plugin never rewrites or merges them.
184
152
 
185
- #### Side-effect imports
153
+ ## Export sorting
186
154
 
187
- The order of side-effect imports (`import 'mod'`) often carries runtime meaning, such as CSS cascade order or polyfills that must load before a framework. The plugin never moves other imports across a side-effect import — imports on each side are sorted independently, and the side-effect import itself stays in place.
155
+ `esmExportSpecifierSort` sorts top-level `export { ... }` and `export type { ... }` entries by name. Aliased entries are sorted by their exported names.
188
156
 
189
- Before:
190
-
191
- <!-- prettier-ignore -->
192
- ```typescript
193
- import Button from './Button';
194
- import App from './App';
195
- import 'normalize.css';
196
- import theme from './theme';
197
- import Icon from './Icon';
198
- ```
199
-
200
- After:
201
-
202
- ```typescript
203
- import App from './App';
204
- import Button from './Button';
205
-
206
- import 'normalize.css';
207
-
208
- import Icon from './Icon';
209
- import theme from './theme';
210
- ```
211
-
212
- Imports on each side are sorted independently. The side-effect import itself stays in place.
213
-
214
- Sorting rules:
215
-
216
- - Imports are classified into groups. Within each group they are sorted alphabetically
217
- - Default group order: `builtin` → `external` → `parent` → `sibling` → `index`
218
- - A blank line is inserted between groups by default. Disable with `importOrderSeparation`
219
- - `type` imports are split into their own statement by default. Use `importOrderTypeImports` to inline them instead
220
- - Multiple imports from the same source are merged into one by default. Disable with `importOrderMergeDuplicates`
221
- - Side-effect imports (`import 'mod'`) carry semantic order and are never moved. Imports on either side are sorted independently
222
-
223
- ### Exports
224
-
225
- By default, named specifiers inside `export { … }` are sorted alphabetically.
226
-
227
- Before:
157
+ Before sorting:
228
158
 
229
159
  <!-- prettier-ignore -->
230
160
  ```typescript
231
161
  export { useState, useEffect, type FC } from 'react';
232
162
  ```
233
163
 
234
- After:
164
+ After sorting:
235
165
 
236
166
  ```typescript
237
167
  export { type FC, useEffect, useState } from 'react';
238
168
  ```
239
169
 
240
- The plugin only reorders what's inside the braces. It doesn't move the export statement itself and doesn't merge two same-source exports. Set `exportOrder` to `false` to disable this behavior.
241
-
242
- Sorting rules:
170
+ The plugin does not move or merge export declarations. If an export list contains comments, the declaration remains unchanged to preserve comment placement.
243
171
 
244
- - Named specifiers inside `export { … }` and `export type { … }` are sorted alphabetically
245
- - The position of the export statement in the file is not changed
246
- - Multiple export statements from the same source are not merged
172
+ ## `package.json` sorting
247
173
 
248
- ### package.json
174
+ `package.json` field order follows [the default rules from sort-package-json 4.0.0](https://github.com/keithamus/sort-package-json/blob/v4.0.0/defaultRules.md). Nested content such as `scripts`, `exports`, and dependency fields follows the same version's rules.
249
175
 
250
- With the default config, the output looks like this.
251
-
252
- Before:
176
+ Before sorting:
253
177
 
254
178
  <!-- prettier-ignore -->
255
179
  ```json
256
180
  {
257
181
  "version": "1.0.0",
258
- "keywords": ["sort", "prettier", "plugin"],
259
- "name": "demo",
182
+ "name": "example",
260
183
  "dependencies": {
261
- "typescript": "^6.0.0",
262
- "prettier": "^3.0.0"
184
+ "typescript": "^7.0.0",
185
+ "prettier": "^3.9.0"
263
186
  }
264
187
  }
265
188
  ```
266
189
 
267
- After:
190
+ After sorting:
268
191
 
269
192
  ```json
270
193
  {
271
- "name": "demo",
194
+ "name": "example",
272
195
  "version": "1.0.0",
273
- "keywords": ["plugin", "prettier", "sort"],
274
196
  "dependencies": {
275
- "prettier": "^3.0.0",
276
- "typescript": "^6.0.0"
197
+ "prettier": "^3.9.0",
198
+ "typescript": "^7.0.0"
277
199
  }
278
200
  }
279
201
  ```
280
202
 
281
- Top-level key order follows the field list maintained by [sort-package-json](https://github.com/keithamus/sort-package-json), staying aligned with widely adopted community conventions.
282
-
283
- Rules the plugin follows:
203
+ Setting `packageSort` to `false` disables key sorting only. Prettier still formats the file as usual.
284
204
 
285
- - Top-level keys are reordered to the canonical sequence (`name` → `version` → ... → `dependencies`)
286
- - Top-level string-only arrays are sorted alphabetically, e.g. `keywords`, `files`
287
- - `dependencies`, `devDependencies`, `peerDependencies` and other dependency maps are always sorted alphabetically, even if `packageJsonOrder` is set to `false`, because `npm install` rewrites them in alphabetical order every time
288
- - `scripts`, `exports`, `imports` and other nested objects are not sorted recursively, because their key order carries runtime semantics
289
- - Use `packageJsonOrderExcludeKeys` to opt specific top-level keys out of sorting entirely
290
-
291
- ## Options
205
+ ## TypeScript configuration
292
206
 
293
- Prettier plugin options are flat, so these options are prefixed with `importOrder`, `exportOrder`, or `packageJsonOrder`.
294
-
295
- | Option | Description | Default |
296
- | ----------------------------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------------- |
297
- | `importOrder` | Sort `import` declarations in JS / TS | `true` |
298
- | `importOrderGroups` | Group order. Valid values: `builtin`, `external`, `internal`, `parent`, `sibling`, `index` | `["builtin", "external", "parent", "sibling", "index"]` |
299
- | `importOrderSeparation` | Insert a blank line between adjacent groups | `true` |
300
- | `importOrderTypeImports` | How to place `type` imports: `separate`, `inline-first`, `inline-last`, `mixed` | `"separate"` |
301
- | `importOrderMergeDuplicates` | Merge multiple `import` statements from the same source (except side-effect imports) | `true` |
302
- | `exportOrder` | Sort named specifiers inside `export { … }` alphabetically | `true` |
303
- | `packageJsonOrder` | Sort top-level keys and string arrays in `package.json` | `true` |
304
- | `packageJsonOrderExcludeKeys` | Top-level `package.json` keys to leave untouched | `[]` |
305
-
306
- ## Example
307
-
308
- ```json
309
- {
310
- "plugins": ["prettier-plugin-sort"],
311
- "importOrderGroups": [
312
- "builtin",
313
- "external",
314
- "internal",
315
- "parent",
316
- "sibling",
317
- "index"
318
- ],
319
- "importOrderTypeImports": "inline-last",
320
- "packageJsonOrderExcludeKeys": ["contributes"]
321
- }
322
- ```
323
-
324
- ## Type hints
325
-
326
- If you write your Prettier config in a `.ts` or `.js` file, you can reuse the `SortOptions` type exported by the plugin to get completion and validation.
327
-
328
- ### In a `.ts` file
207
+ The package also exports `SortOptions`, `ImportGroup`, and `TypeImportStyle` for use in TypeScript configuration files:
329
208
 
330
209
  ```typescript
331
210
  import { type Config } from 'prettier';
@@ -333,7 +212,7 @@ import { type SortOptions } from 'prettier-plugin-sort';
333
212
 
334
213
  export default {
335
214
  plugins: ['prettier-plugin-sort'],
336
- importOrderGroups: [
215
+ esmImportGroups: [
337
216
  'builtin',
338
217
  'external',
339
218
  'internal',
@@ -341,46 +220,6 @@ export default {
341
220
  'sibling',
342
221
  'index',
343
222
  ],
344
- importOrderTypeImports: 'inline-last',
345
- packageJsonOrderExcludeKeys: ['contributes'],
223
+ esmImportTypeStyle: 'inline-last',
346
224
  } satisfies Config & SortOptions;
347
225
  ```
348
-
349
- ### In a `.js` file
350
-
351
- ```js
352
- /** @type {import('prettier').Config & import('prettier-plugin-sort').SortOptions} */
353
- const config = {
354
- plugins: ['prettier-plugin-sort'],
355
- importOrderGroups: [
356
- 'builtin',
357
- 'external',
358
- 'internal',
359
- 'parent',
360
- 'sibling',
361
- 'index',
362
- ],
363
- importOrderTypeImports: 'inline-last',
364
- packageJsonOrderExcludeKeys: ['contributes'],
365
- };
366
-
367
- export default config;
368
- ```
369
-
370
- The `ImportGroup` and `TypeImportsStyle` literal types are also exported if you only need those.
371
-
372
- ## Motivation
373
-
374
- Before adopting Prettier, I relied on IDE-native sorting features to keep my code organized. As I started switching between different IDEs, I wanted a portable, unified configuration, so I brought ESLint and Prettier into my projects.
375
-
376
- Prettier doesn't provide sorting out of the box. To sort imports I had to install `prettier-plugin-organize-imports`. To sort `package.json` I had to install `prettier-plugin-packagejson`. The fragmented experience bothered me.
377
-
378
- I ignored this for a long time while focusing on actual development. Recently, though, I needed to control how `import type` was inlined and found that `prettier-plugin-organize-imports` didn't support it. On top of that, `prettier-plugin-packagejson`, built on `sort-package-json`, carries many dependencies that are redundant for a plugin. That's when I decided to build my own.
379
-
380
- `prettier-plugin-sort` isn't meant to replace anything. It's about giving developers more options. Prettier is used almost entirely for JS/TS code, and every JS project has a `package.json`, so the plugin covers these two fundamental sorting tasks. The goal is to let JS developers work out of the box with minimal mental overhead (support for `tsconfig.json` sorting may be added in a future release). If you have other sorting needs, you can still install something like `prettier-plugin-css-order` alongside it. There's no conflict.
381
-
382
- ## Credits
383
-
384
- - `eslint-plugin-import`: https://github.com/import-js/eslint-plugin-import
385
- - `typescript-eslint`: https://github.com/typescript-eslint/typescript-eslint
386
- - `sort-package-json`: https://github.com/keithamus/sort-package-json