prettier-plugin-sort 0.0.0 → 0.0.2
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/LICENSE +21 -0
- package/README.md +357 -0
- package/README.zh.md +362 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +736 -0
- package/package.json +58 -12
- package/index.js +0 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yuki
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
# prettier-plugin-sort
|
|
2
|
+
|
|
3
|
+
A [Prettier](https://prettier.io/) plugin focused on sorting.
|
|
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
|
|
9
|
+
|
|
10
|
+
Read this in other languages: English | [中文](./README.zh.md)
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```shell
|
|
15
|
+
npm i -D prettier prettier-plugin-sort
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Enable it in your Prettier config:
|
|
19
|
+
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"plugins": ["prettier-plugin-sort"]
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Then run Prettier as usual, for example `npx prettier --write .`.
|
|
27
|
+
|
|
28
|
+
## What gets sorted
|
|
29
|
+
|
|
30
|
+
### Imports
|
|
31
|
+
|
|
32
|
+
#### Grouping and sorting
|
|
33
|
+
|
|
34
|
+
With the default config, imports are grouped and ordered like this.
|
|
35
|
+
|
|
36
|
+
Before:
|
|
37
|
+
|
|
38
|
+
<!-- prettier-ignore -->
|
|
39
|
+
```typescript
|
|
40
|
+
import App from './App.tsx';
|
|
41
|
+
import fs from 'node:fs';
|
|
42
|
+
import lodash from 'lodash';
|
|
43
|
+
import path from 'node:path';
|
|
44
|
+
import react from 'react';
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
After:
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import fs from 'node:fs';
|
|
51
|
+
import path from 'node:path';
|
|
52
|
+
|
|
53
|
+
import lodash from 'lodash';
|
|
54
|
+
import react from 'react';
|
|
55
|
+
|
|
56
|
+
import App from './App.tsx';
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Each import belongs to a group. For example, `node:fs` is a `builtin` (covers Node.js / Bun / Deno built-ins), while `react` and `lodash` are `external` npm packages. The plugin first classifies each import by group, then sorts alphabetically within each group.
|
|
60
|
+
|
|
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.
|
|
62
|
+
|
|
63
|
+
The default import config is:
|
|
64
|
+
|
|
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:
|
|
76
|
+
|
|
77
|
+
| Group | Matches | Examples |
|
|
78
|
+
| ---------- | ----------------------------------------------------------- | ---------------------------------- |
|
|
79
|
+
| `builtin` | `node:*`, `bun:*`, `deno:*`, and unprefixed Node built-ins | `node:fs`, `path` |
|
|
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` |
|
|
85
|
+
|
|
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:
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"plugins": ["prettier-plugin-sort"],
|
|
93
|
+
"importOrderGroups": [
|
|
94
|
+
"builtin",
|
|
95
|
+
"external",
|
|
96
|
+
"internal",
|
|
97
|
+
"parent",
|
|
98
|
+
"sibling",
|
|
99
|
+
"index"
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
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
|
|
127
|
+
|
|
128
|
+
By default the plugin splits `type` imports into their own statement.
|
|
129
|
+
|
|
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
|
+
```
|
|
144
|
+
|
|
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).
|
|
146
|
+
|
|
147
|
+
Using `import { c, type B, a } from 'mod';` as an example:
|
|
148
|
+
|
|
149
|
+
| Mode | Result |
|
|
150
|
+
| -------------- | ---------------------------------------------------------------- |
|
|
151
|
+
| `separate` | `import type { B } from 'mod';`<br>`import { a, c } from 'mod';` |
|
|
152
|
+
| `inline-first` | `import { type B, a, c } from 'mod';` |
|
|
153
|
+
| `inline-last` | `import { a, c, type B } from 'mod';` |
|
|
154
|
+
| `mixed` | `import { a, type B, c } from 'mod';` |
|
|
155
|
+
|
|
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
|
+
```
|
|
169
|
+
|
|
170
|
+
After:
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
import { useEffect, useState } from 'react';
|
|
174
|
+
```
|
|
175
|
+
|
|
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:
|
|
177
|
+
|
|
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';`
|
|
182
|
+
|
|
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.
|
|
184
|
+
|
|
185
|
+
Sorting rules:
|
|
186
|
+
|
|
187
|
+
- Imports are classified into groups. Within each group they are sorted alphabetically
|
|
188
|
+
- Default group order: `builtin` → `external` → `parent` → `sibling` → `index`
|
|
189
|
+
- A blank line is inserted between groups by default. Disable with `importOrderSeparation`
|
|
190
|
+
- `type` imports are split into their own statement by default. Use `importOrderTypeImports` to inline them instead
|
|
191
|
+
- Multiple imports from the same source are merged into one by default. Disable with `importOrderMergeDuplicates`
|
|
192
|
+
- Side-effect imports (`import 'mod'`) are never merged or moved across groups
|
|
193
|
+
|
|
194
|
+
### Exports
|
|
195
|
+
|
|
196
|
+
By default, named specifiers inside `export { … }` are sorted alphabetically.
|
|
197
|
+
|
|
198
|
+
Before:
|
|
199
|
+
|
|
200
|
+
<!-- prettier-ignore -->
|
|
201
|
+
```typescript
|
|
202
|
+
export { useState, useEffect, type FC } from 'react';
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
After:
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
export { type FC, useEffect, useState } from 'react';
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
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.
|
|
212
|
+
|
|
213
|
+
Sorting rules:
|
|
214
|
+
|
|
215
|
+
- Named specifiers inside `export { … }` and `export type { … }` are sorted alphabetically
|
|
216
|
+
- The position of the export statement in the file is not changed
|
|
217
|
+
- Multiple export statements from the same source are not merged
|
|
218
|
+
|
|
219
|
+
### package.json
|
|
220
|
+
|
|
221
|
+
With the default config, the output looks like this.
|
|
222
|
+
|
|
223
|
+
Before:
|
|
224
|
+
|
|
225
|
+
<!-- prettier-ignore -->
|
|
226
|
+
```json
|
|
227
|
+
{
|
|
228
|
+
"version": "1.0.0",
|
|
229
|
+
"keywords": ["sort", "prettier", "plugin"],
|
|
230
|
+
"name": "demo",
|
|
231
|
+
"dependencies": {
|
|
232
|
+
"typescript": "^6.0.0",
|
|
233
|
+
"prettier": "^3.0.0"
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
After:
|
|
239
|
+
|
|
240
|
+
```json
|
|
241
|
+
{
|
|
242
|
+
"name": "demo",
|
|
243
|
+
"version": "1.0.0",
|
|
244
|
+
"keywords": ["plugin", "prettier", "sort"],
|
|
245
|
+
"dependencies": {
|
|
246
|
+
"prettier": "^3.0.0",
|
|
247
|
+
"typescript": "^6.0.0"
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
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.
|
|
253
|
+
|
|
254
|
+
Rules the plugin follows:
|
|
255
|
+
|
|
256
|
+
- Top-level keys are reordered to the canonical sequence (`name` → `version` → ... → `dependencies`)
|
|
257
|
+
- Top-level string-only arrays are sorted alphabetically, e.g. `keywords`, `files`
|
|
258
|
+
- `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
|
|
259
|
+
- `scripts`, `exports`, `imports` and other nested objects are not sorted recursively, because their key order carries runtime semantics
|
|
260
|
+
- Use `packageJsonOrderExcludeKeys` to opt specific top-level keys out of sorting entirely
|
|
261
|
+
|
|
262
|
+
## Options
|
|
263
|
+
|
|
264
|
+
Prettier plugin options are flat, so these options are prefixed with `importOrder`, `exportOrder`, or `packageJsonOrder`.
|
|
265
|
+
|
|
266
|
+
| Option | Description | Default |
|
|
267
|
+
| ----------------------------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------------- |
|
|
268
|
+
| `importOrder` | Sort `import` declarations in JS / TS | `true` |
|
|
269
|
+
| `importOrderGroups` | Group order. Valid values: `builtin`, `external`, `internal`, `parent`, `sibling`, `index` | `["builtin", "external", "parent", "sibling", "index"]` |
|
|
270
|
+
| `importOrderSeparation` | Insert a blank line between adjacent groups | `true` |
|
|
271
|
+
| `importOrderTypeImports` | How to place `type` imports: `separate`, `inline-first`, `inline-last`, `mixed` | `"separate"` |
|
|
272
|
+
| `importOrderMergeDuplicates` | Merge multiple `import` statements from the same source (except side-effect imports) | `true` |
|
|
273
|
+
| `exportOrder` | Sort named specifiers inside `export { … }` alphabetically | `true` |
|
|
274
|
+
| `packageJsonOrder` | Sort top-level keys and string arrays in `package.json` | `true` |
|
|
275
|
+
| `packageJsonOrderExcludeKeys` | Top-level `package.json` keys to leave untouched | `[]` |
|
|
276
|
+
|
|
277
|
+
## Example
|
|
278
|
+
|
|
279
|
+
```json
|
|
280
|
+
{
|
|
281
|
+
"plugins": ["prettier-plugin-sort"],
|
|
282
|
+
"importOrderGroups": [
|
|
283
|
+
"builtin",
|
|
284
|
+
"external",
|
|
285
|
+
"internal",
|
|
286
|
+
"parent",
|
|
287
|
+
"sibling",
|
|
288
|
+
"index"
|
|
289
|
+
],
|
|
290
|
+
"importOrderTypeImports": "inline-last",
|
|
291
|
+
"packageJsonOrderExcludeKeys": ["contributes"]
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## Type hints
|
|
296
|
+
|
|
297
|
+
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.
|
|
298
|
+
|
|
299
|
+
### In a `.ts` file
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
import { type Config } from 'prettier';
|
|
303
|
+
import { type SortOptions } from 'prettier-plugin-sort';
|
|
304
|
+
|
|
305
|
+
export default {
|
|
306
|
+
plugins: ['prettier-plugin-sort'],
|
|
307
|
+
importOrderGroups: [
|
|
308
|
+
'builtin',
|
|
309
|
+
'external',
|
|
310
|
+
'internal',
|
|
311
|
+
'parent',
|
|
312
|
+
'sibling',
|
|
313
|
+
'index',
|
|
314
|
+
],
|
|
315
|
+
importOrderTypeImports: 'inline-last',
|
|
316
|
+
packageJsonOrderExcludeKeys: ['contributes'],
|
|
317
|
+
} satisfies Config & SortOptions;
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### In a `.js` file
|
|
321
|
+
|
|
322
|
+
```js
|
|
323
|
+
/** @type {import('prettier').Config & import('prettier-plugin-sort').SortOptions} */
|
|
324
|
+
const config = {
|
|
325
|
+
plugins: ['prettier-plugin-sort'],
|
|
326
|
+
importOrderGroups: [
|
|
327
|
+
'builtin',
|
|
328
|
+
'external',
|
|
329
|
+
'internal',
|
|
330
|
+
'parent',
|
|
331
|
+
'sibling',
|
|
332
|
+
'index',
|
|
333
|
+
],
|
|
334
|
+
importOrderTypeImports: 'inline-last',
|
|
335
|
+
packageJsonOrderExcludeKeys: ['contributes'],
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
export default config;
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
The `ImportGroup` and `TypeImportsStyle` literal types are also exported if you only need those.
|
|
342
|
+
|
|
343
|
+
## Motivation
|
|
344
|
+
|
|
345
|
+
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.
|
|
346
|
+
|
|
347
|
+
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.
|
|
348
|
+
|
|
349
|
+
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.
|
|
350
|
+
|
|
351
|
+
`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.
|
|
352
|
+
|
|
353
|
+
## Credits
|
|
354
|
+
|
|
355
|
+
- `eslint-plugin-import`: https://github.com/import-js/eslint-plugin-import
|
|
356
|
+
- `typescript-eslint`: https://github.com/typescript-eslint/typescript-eslint
|
|
357
|
+
- `sort-package-json`: https://github.com/keithamus/sort-package-json
|