@1adybug/prettier-plugin-sort-imports 0.0.1

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 ADDED
@@ -0,0 +1,453 @@
1
+ # Prettier Plugin Import Sorts
2
+
3
+ [中文文档](https://github.com/1adybug/prettier-plugin-sort-imports/blob/main/README.zh-CN.md)
4
+
5
+ A powerful Prettier plugin for intelligently grouping and sorting import statements in JavaScript/TypeScript files.
6
+
7
+ ## Features
8
+
9
+ - ✅ **Smart Sorting**: Support for sorting both import modules and import contents
10
+ - ✅ **Flexible Grouping**: Customizable grouping rules based on module type, path, etc.
11
+ - ✅ **TypeScript Support**: Full support for TypeScript `type` imports
12
+ - ✅ **Comment Preservation**: Comments follow their associated import statements
13
+ - ✅ **Side Effect Handling**: Configurable sorting behavior for side effect imports
14
+ - ✅ **Unused Import Removal**: Optional automatic removal of unused imports
15
+ - ✅ **Factory Function Pattern**: Support for custom functions in configuration files
16
+
17
+ ## Quick Start
18
+
19
+ ### Installation
20
+
21
+ ```bash
22
+ npm install prettier-plugin-import-sorts --save-dev
23
+ ```
24
+
25
+ ### Basic Configuration
26
+
27
+ Add the plugin to your `prettier.config.mjs`:
28
+
29
+ ```javascript
30
+ export default {
31
+ plugins: ["prettier-plugin-import-sorts"],
32
+ }
33
+ ```
34
+
35
+ ### Usage
36
+
37
+ ```bash
38
+ npx prettier --write "src/**/*.{js,ts,jsx,tsx}"
39
+ ```
40
+
41
+ ## Usage Examples
42
+
43
+ ### Basic Sorting
44
+
45
+ ```typescript
46
+
47
+ ```
48
+
49
+ ### Custom Grouping and Sorting
50
+
51
+ ```javascript
52
+ // prettier.config.mjs
53
+ import { createPlugin } from "prettier-plugin-import-sorts"
54
+
55
+ export default {
56
+ plugins: [
57
+ createPlugin({
58
+ // Custom grouping: group by module type
59
+ getGroup: statement => {
60
+ if (statement.path.startsWith("react")) return "react"
61
+ if (!statement.path.startsWith(".")) return "external"
62
+ return "local"
63
+ },
64
+ // Specify group order
65
+ sortGroup: (a, b) => {
66
+ const order = ["react", "external", "local"]
67
+ return order.indexOf(a.name) - order.indexOf(b.name)
68
+ },
69
+ // Add blank lines between groups
70
+ separator: "",
71
+ }),
72
+ ],
73
+ }
74
+ ```
75
+
76
+ Result:
77
+
78
+ ```typescript
79
+ import "./styles.css"
80
+ ```
81
+
82
+ ## API Documentation
83
+
84
+ ### Type Definitions
85
+
86
+ #### ImportContent
87
+
88
+ Definition of import content:
89
+
90
+ ```typescript
91
+ interface ImportContent {
92
+ /** Name of the imported content */
93
+ name: string
94
+ /** Alias of the imported content */
95
+ alias?: string
96
+ /** Type of the imported content, only explicitly marked type imports belong to type */
97
+ type: "type" | "variable"
98
+ }
99
+ ```
100
+
101
+ #### ImportStatement
102
+
103
+ Definition of import statement:
104
+
105
+ ```typescript
106
+ interface ImportStatement {
107
+ /** Module path of the import, can be relative or absolute */
108
+ path: string
109
+ /** Whether it's an export statement, defaults to false */
110
+ isExport: boolean
111
+ /** Whether it's a side effect import, defaults to false */
112
+ isSideEffect: boolean
113
+ /** Import contents */
114
+ importContents: ImportContent[]
115
+ }
116
+ ```
117
+
118
+ #### Group
119
+
120
+ Group definition:
121
+
122
+ ```typescript
123
+ interface Group {
124
+ /** Group name, defaults to "default" */
125
+ name: string
126
+ /** Whether it's a side effect group, defaults to false */
127
+ isSideEffect: boolean
128
+ /** List of import statements in the group */
129
+ importStatements: ImportStatement[]
130
+ }
131
+ ```
132
+
133
+ #### PluginConfig
134
+
135
+ Plugin configuration:
136
+
137
+ ```typescript
138
+ interface PluginConfig {
139
+ /** Custom grouping function */
140
+ getGroup?: (importStatement: ImportStatement) => string
141
+ /** Custom group sorting function */
142
+ sortGroup?: (a: Group, b: Group) => number
143
+ /** Custom import statement sorting function */
144
+ sortImportStatement?: (a: ImportStatement, b: ImportStatement) => number
145
+ /** Custom import content sorting function */
146
+ sortImportContent?: (a: ImportContent, b: ImportContent) => number
147
+ /** Separator between groups */
148
+ separator?: string | ((group: Group, index: number) => string | undefined)
149
+ /** Whether to sort side effect imports, defaults to false */
150
+ sortSideEffect?: boolean
151
+ /** Whether to remove unused imports, defaults to false */
152
+ removeUnusedImports?: boolean
153
+ }
154
+ ```
155
+
156
+ ## Configuration Options
157
+
158
+ ### Method 1: Simple Configuration
159
+
160
+ Configure basic options via Prettier config file:
161
+
162
+ ```javascript
163
+ export default {
164
+ plugins: ["prettier-plugin-import-sorts"],
165
+ importSortSideEffect: false, // Whether to sort side effect imports
166
+ importSortSeparator: "", // Group separator
167
+ importSortRemoveUnused: false, // Whether to remove unused imports
168
+ }
169
+ ```
170
+
171
+ ### Method 2: Advanced Configuration (Factory Function)
172
+
173
+ Use `createPlugin` function to pass custom functions:
174
+
175
+ ```javascript
176
+ import { createPlugin } from "prettier-plugin-import-sorts"
177
+
178
+ export default {
179
+ plugins: [
180
+ createPlugin({
181
+ getGroup: statement => {
182
+ /* Custom grouping logic */
183
+ },
184
+ sortGroup: (a, b) => {
185
+ /* Custom sorting */
186
+ },
187
+ sortImportStatement: (a, b) => {
188
+ /* Custom sorting */
189
+ },
190
+ sortImportContent: (a, b) => {
191
+ /* Custom sorting */
192
+ },
193
+ separator: "",
194
+ sortSideEffect: true,
195
+ removeUnusedImports: false,
196
+ }),
197
+ ],
198
+ }
199
+ ```
200
+
201
+ ### importSortRemoveUnused
202
+
203
+ Whether to remove unused imports, defaults to `false`.
204
+
205
+ **Default behavior (false)**: Keeps all imports.
206
+
207
+ **When enabled (true)**: Automatically analyzes code and removes unused imports.
208
+
209
+ ```typescript
210
+ // Before sorting
211
+ import React, { useState, useEffect } from "react"
212
+ import { Button, Input } from "antd"
213
+ import { helper } from "./utils"
214
+
215
+ function MyComponent() {
216
+ const [count, setCount] = useState(0)
217
+ return <Button>Click me</Button>
218
+ }
219
+
220
+ // After sorting (with removeUnusedImports enabled)
221
+ import React, { useState } from "react"
222
+ import { Button } from "antd"
223
+
224
+ function MyComponent() {
225
+ const [count, setCount] = useState(0)
226
+ return <Button>Click me</Button>
227
+ }
228
+ ```
229
+
230
+ **Notes**:
231
+
232
+ - Side effect imports (e.g., `import "./styles.css"`) will not be removed
233
+ - Export statements (e.g., `export { x } from "module"`) will not be removed
234
+ - Analysis is AST-based and identifies actually used identifiers in code
235
+ - Supports identifying JSX components, TypeScript type references, etc.
236
+
237
+ ### importSortSideEffect
238
+
239
+ Whether to sort side effect imports, defaults to `false`.
240
+
241
+ **Default behavior (false)**: Side effect imports act as separators, imports between separators are sorted independently.
242
+
243
+ ```typescript
244
+ import "f-side-effect"
245
+ import "f-side-effect"
246
+ ```
247
+
248
+ **When enabled (true)**: Side effect imports also participate in sorting.
249
+
250
+ ```typescript
251
+ import "f-side-effect"
252
+ import "f-side-effect"
253
+ ```
254
+
255
+ ### separator
256
+
257
+ Separator between groups, defaults to `undefined` (no separator).
258
+
259
+ Can be a string or function:
260
+
261
+ ```javascript
262
+ // String: add blank lines between all groups
263
+ separator: ""
264
+
265
+ // Function: flexible control
266
+ separator: (group, index) => {
267
+ // No separator for the first group
268
+ if (index === 0) return undefined
269
+ // Add blank lines for other groups
270
+ return ""
271
+ }
272
+ ```
273
+
274
+ ## Default Sorting Rules
275
+
276
+ ### Import Content Sorting
277
+
278
+ **Default behavior** (when custom `sortImportContent` is not provided):
279
+
280
+ 1. Default imports always come first
281
+ 2. Namespace imports (`import * as`) come after default imports
282
+ 3. Named imports are sorted by `type` priority, then alphabetically by final import name
283
+
284
+ ```typescript
285
+
286
+ ```
287
+
288
+ **Custom behavior**:
289
+
290
+ If a custom `sortImportContent` function is provided, the plugin will **fully follow your sorting logic**:
291
+
292
+ ```javascript
293
+ createPlugin({
294
+ // Fully alphabetical order, no distinction between type and variable
295
+ sortImportContent: (a, b) => {
296
+ const aName = a.alias ?? a.name
297
+ const bName = b.alias ?? b.name
298
+ return aName.localeCompare(bName)
299
+ },
300
+ })
301
+ ```
302
+
303
+ ```typescript
304
+
305
+ ```
306
+
307
+ ### Import Statement Sorting
308
+
309
+ Import statements are sorted alphabetically by module path:
310
+
311
+ ```typescript
312
+
313
+ ```
314
+
315
+ ### Comment Handling
316
+
317
+ Comments follow the import statements they are attached to:
318
+
319
+ ```typescript
320
+
321
+ ```
322
+
323
+ ## Implementation Details
324
+
325
+ ### Core Modules
326
+
327
+ #### 1. Type Definitions (`src/types.ts`)
328
+
329
+ Defines all interface types: ImportContent, ImportStatement, Group, PluginConfig, and various function types.
330
+
331
+ #### 2. Parser (`src/parser.ts`)
332
+
333
+ Uses `@babel/parser` to parse source code and extract import/export statements:
334
+
335
+ - Parse source code into AST
336
+ - Traverse AST to find all import and export statements
337
+ - Identify import types: default import, named import, namespace import, side effect import
338
+ - Identify TypeScript `type` import markers
339
+ - Extract and preserve comments above import statements
340
+ - Record position information of import statements
341
+
342
+ #### 3. Sorter (`src/sorter.ts`)
343
+
344
+ Implements grouping and sorting logic:
345
+
346
+ - Group import statements according to `getGroup` function
347
+ - If `sortSideEffect` is false, treat side effect imports as separators
348
+ - Use various sorting functions to sort groups, import statements, and import contents
349
+ - Support fully customizable sorting logic
350
+
351
+ #### 4. Formatter (`src/formatter.ts`)
352
+
353
+ Converts sorted import statements back to code strings:
354
+
355
+ - Generate corresponding import/export code from `ImportStatement`
356
+ - Handle formatting of default imports, named imports, namespace imports
357
+ - Handle `type` import formatting
358
+ - Insert separators between groups according to `separator` configuration
359
+ - Maintain comment associations
360
+
361
+ #### 5. Plugin Entry (`src/index.ts`)
362
+
363
+ Implements Prettier plugin standard interface:
364
+
365
+ - Extends existing babel/typescript parsers
366
+ - Supports factory function pattern
367
+ - Integrates parser, sorter, formatter
368
+ - Only processes consecutive import statement blocks at the beginning of files
369
+
370
+ #### 6. Analyzer (`src/analyzer.ts`)
371
+
372
+ Analyzes identifiers used in code and filters unused imports:
373
+
374
+ - Uses `@babel/traverse` to traverse AST
375
+ - Collects all identifiers used in code (variables, functions, JSX components, type references, etc.)
376
+ - Filters import statements, keeping only import contents used in code
377
+ - Supports identifying aliases, default imports, namespace imports, etc.
378
+
379
+ ### Tech Stack
380
+
381
+ - **Build Tool**: rslib
382
+ - **Parser**: @babel/parser
383
+ - **AST Traversal**: @babel/traverse
384
+ - **AST Types**: @babel/types
385
+ - **Plugin System**: Prettier 3.x
386
+
387
+ ### Advantages of Factory Function Pattern
388
+
389
+ Prettier natively cannot accept functions as configuration parameters (because configurations need to be serializable). This plugin cleverly solves this problem through the factory function pattern:
390
+
391
+ ```javascript
392
+ // Factory function is called in config file, returning a plugin instance
393
+ import { createPlugin } from "prettier-plugin-import-sorts"
394
+
395
+ export default {
396
+ plugins: [
397
+ createPlugin({
398
+ // Can pass functions!
399
+ getGroup: statement => {
400
+ /* ... */
401
+ },
402
+ }),
403
+ ],
404
+ }
405
+ ```
406
+
407
+ This maintains configuration flexibility while not violating Prettier's configuration system limitations.
408
+
409
+ ## Notes
410
+
411
+ 1. **Only processes consecutive import/export statement blocks at the beginning of files**
412
+ - After encountering non-import/export statements, subsequent imports will not be processed
413
+
414
+ 2. **Supported File Types**
415
+ - JavaScript: `.js`, `.jsx`, `.mjs`, `.cjs`, `.mjsx`, `.cjsx`
416
+ - TypeScript: `.ts`, `.tsx`, `.mts`, `.cts`, `.mtsx`, `.ctsx`
417
+
418
+ 3. **Does not support CommonJS `require` statements**
419
+ - Only supports ES6 module syntax (import/export)
420
+
421
+ 4. **Custom Sorting Functions**
422
+ - When providing custom `sortImportContent`, the plugin will fully follow your logic
423
+ - Will not enforce rules like default imports first or types first
424
+
425
+ ## Project Status
426
+
427
+ ✅ **Complete and Ready to Use**
428
+
429
+ All core features have been implemented and tested. The plugin works properly and can be integrated into any project using Prettier.
430
+
431
+ ### Verified Scenarios
432
+
433
+ 1. ✅ Basic import sorting (alphabetically)
434
+ 2. ✅ Side effect imports as separators
435
+ 3. ✅ Side effect import sorting (with option enabled)
436
+ 4. ✅ Comments follow import statements
437
+ 5. ✅ TypeScript type imports prioritized
438
+ 6. ✅ Default and namespace import positions
439
+ 7. ✅ Mixed imports (default + named)
440
+ 8. ✅ Import contents sorted by alias
441
+ 9. ✅ Custom sorting logic
442
+
443
+ ## Next Steps (Optional)
444
+
445
+ 1. Add unit tests (using Jest or Vitest)
446
+ 2. Add CI/CD configuration
447
+ 3. Publish to npm
448
+ 4. Add more examples
449
+ 5. Support more configuration options (e.g., ignoring specific imports)
450
+
451
+ ## License
452
+
453
+ MIT