@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.
@@ -0,0 +1,453 @@
1
+ # Prettier Plugin Import Sorts
2
+
3
+ [English](https://github.com/1adybug/prettier-plugin-sort-imports/blob/main/README.md)
4
+
5
+ 一个功能强大的 Prettier 插件,用于对 JavaScript/TypeScript 文件的导入语句进行智能分组和排序。
6
+
7
+ ## 特性
8
+
9
+ - ✅ **智能排序**:支持对导入模块和导入内容进行排序
10
+ - ✅ **灵活分组**:自定义分组规则,支持按模块类型、路径等分组
11
+ - ✅ **TypeScript 支持**:完整支持 TypeScript 的 `type` 导入
12
+ - ✅ **注释保留**:注释会跟随对应的导入语句移动
13
+ - ✅ **副作用处理**:可配置副作用导入的排序行为
14
+ - ✅ **未使用导入删除**:可选的自动删除未使用的导入功能
15
+ - ✅ **工厂函数模式**:支持在配置文件中使用自定义函数
16
+
17
+ ## 快速开始
18
+
19
+ ### 安装
20
+
21
+ ```bash
22
+ npm install prettier-plugin-import-sorts --save-dev
23
+ ```
24
+
25
+ ### 基础配置
26
+
27
+ 在 `prettier.config.mjs` 中添加插件:
28
+
29
+ ```javascript
30
+ export default {
31
+ plugins: ["prettier-plugin-import-sorts"],
32
+ }
33
+ ```
34
+
35
+ ### 运行
36
+
37
+ ```bash
38
+ npx prettier --write "src/**/*.{js,ts,jsx,tsx}"
39
+ ```
40
+
41
+ ## 使用示例
42
+
43
+ ### 基本排序
44
+
45
+ ```typescript
46
+
47
+ ```
48
+
49
+ ### 自定义分组和排序
50
+
51
+ ```javascript
52
+ // prettier.config.mjs
53
+ import { createPlugin } from "prettier-plugin-import-sorts"
54
+
55
+ export default {
56
+ plugins: [
57
+ createPlugin({
58
+ // 自定义分组:按模块类型分组
59
+ getGroup: statement => {
60
+ if (statement.path.startsWith("react")) return "react"
61
+ if (!statement.path.startsWith(".")) return "external"
62
+ return "local"
63
+ },
64
+ // 指定分组顺序
65
+ sortGroup: (a, b) => {
66
+ const order = ["react", "external", "local"]
67
+ return order.indexOf(a.name) - order.indexOf(b.name)
68
+ },
69
+ // 在分组之间添加空行
70
+ separator: "",
71
+ }),
72
+ ],
73
+ }
74
+ ```
75
+
76
+ 结果:
77
+
78
+ ```typescript
79
+ import "./styles.css"
80
+ ```
81
+
82
+ ## API 文档
83
+
84
+ ### 类型定义
85
+
86
+ #### ImportContent
87
+
88
+ 导入内容的定义:
89
+
90
+ ```typescript
91
+ interface ImportContent {
92
+ /** 导入的内容的名称 */
93
+ name: string
94
+ /** 导入的内容的别名 */
95
+ alias?: string
96
+ /** 导入的内容的类型,只有明确在导入前加入了 type 标记的才属于 type 类型 */
97
+ type: "type" | "variable"
98
+ }
99
+ ```
100
+
101
+ #### ImportStatement
102
+
103
+ 导入语句的定义:
104
+
105
+ ```typescript
106
+ interface ImportStatement {
107
+ /** 导入的模块路径,可以是相对路径或绝对路径 */
108
+ path: string
109
+ /** 是否是导出语句,默认为 false */
110
+ isExport: boolean
111
+ /** 是否是副作用导入,默认为 false */
112
+ isSideEffect: boolean
113
+ /** 导入的内容 */
114
+ importContents: ImportContent[]
115
+ }
116
+ ```
117
+
118
+ #### Group
119
+
120
+ 分组定义:
121
+
122
+ ```typescript
123
+ interface Group {
124
+ /** 分组名称,默认为 default */
125
+ name: string
126
+ /** 是否是副作用分组,默认为 false */
127
+ isSideEffect: boolean
128
+ /** 分组对应的导入语句列表 */
129
+ importStatements: ImportStatement[]
130
+ }
131
+ ```
132
+
133
+ #### PluginConfig
134
+
135
+ 插件配置:
136
+
137
+ ```typescript
138
+ interface PluginConfig {
139
+ /** 自定义分组函数 */
140
+ getGroup?: (importStatement: ImportStatement) => string
141
+ /** 自定义分组排序函数 */
142
+ sortGroup?: (a: Group, b: Group) => number
143
+ /** 自定义导入语句排序函数 */
144
+ sortImportStatement?: (a: ImportStatement, b: ImportStatement) => number
145
+ /** 自定义导入内容排序函数 */
146
+ sortImportContent?: (a: ImportContent, b: ImportContent) => number
147
+ /** 分组之间的分隔符 */
148
+ separator?: string | ((group: Group, index: number) => string | undefined)
149
+ /** 是否对副作用导入进行排序,默认为 false */
150
+ sortSideEffect?: boolean
151
+ /** 是否删除未使用的导入,默认为 false */
152
+ removeUnusedImports?: boolean
153
+ }
154
+ ```
155
+
156
+ ## 配置选项
157
+
158
+ ### 方式 1:简单配置
159
+
160
+ 通过 Prettier 配置文件配置基本选项:
161
+
162
+ ```javascript
163
+ export default {
164
+ plugins: ["prettier-plugin-import-sorts"],
165
+ importSortSideEffect: false, // 是否对副作用导入排序
166
+ importSortSeparator: "", // 分组分隔符
167
+ importSortRemoveUnused: false, // 是否删除未使用的导入
168
+ }
169
+ ```
170
+
171
+ ### 方式 2:高级配置(工厂函数)
172
+
173
+ 使用 `createPlugin` 函数可以传递自定义函数:
174
+
175
+ ```javascript
176
+ import { createPlugin } from "prettier-plugin-import-sorts"
177
+
178
+ export default {
179
+ plugins: [
180
+ createPlugin({
181
+ getGroup: statement => {
182
+ /* 自定义分组逻辑 */
183
+ },
184
+ sortGroup: (a, b) => {
185
+ /* 自定义排序 */
186
+ },
187
+ sortImportStatement: (a, b) => {
188
+ /* 自定义排序 */
189
+ },
190
+ sortImportContent: (a, b) => {
191
+ /* 自定义排序 */
192
+ },
193
+ separator: "",
194
+ sortSideEffect: true,
195
+ removeUnusedImports: false,
196
+ }),
197
+ ],
198
+ }
199
+ ```
200
+
201
+ ### importSortRemoveUnused
202
+
203
+ 是否删除未使用的导入,默认为 `false`。
204
+
205
+ **默认行为(false)**:保留所有导入。
206
+
207
+ **开启后(true)**:自动分析代码并删除未使用的导入。
208
+
209
+ ```typescript
210
+ // 排序前
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
+ // 排序后(开启 removeUnusedImports)
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
+ **注意事项**:
231
+
232
+ - 副作用导入(如 `import "./styles.css"`)不会被删除
233
+ - 导出语句(如 `export { x } from "module"`)不会被删除
234
+ - 分析基于 AST,会识别代码中实际使用的标识符
235
+ - 支持识别 JSX 组件、TypeScript 类型引用等
236
+
237
+ ### importSortSideEffect
238
+
239
+ 是否对副作用导入进行排序,默认为 `false`。
240
+
241
+ **默认行为(false)**:副作用导入作为分隔符,分隔符之间的导入独立排序。
242
+
243
+ ```typescript
244
+ import "f-side-effect"
245
+ import "f-side-effect"
246
+ ```
247
+
248
+ **开启后(true)**:副作用导入也会参与排序。
249
+
250
+ ```typescript
251
+ import "f-side-effect"
252
+ import "f-side-effect"
253
+ ```
254
+
255
+ ### separator
256
+
257
+ 分组之间的分隔符,默认为 `undefined`(无分隔符)。
258
+
259
+ 可以是字符串或函数:
260
+
261
+ ```javascript
262
+ // 字符串:在所有分组间添加空行
263
+ separator: ""
264
+
265
+ // 函数:灵活控制
266
+ separator: (group, index) => {
267
+ // 第一个分组不添加分隔符
268
+ if (index === 0) return undefined
269
+ // 其他分组添加空行
270
+ return ""
271
+ }
272
+ ```
273
+
274
+ ## 默认排序规则
275
+
276
+ ### 导入内容排序
277
+
278
+ **默认行为**(未提供自定义 `sortImportContent` 时):
279
+
280
+ 1. 默认导入始终在最前面
281
+ 2. 命名空间导入(`import * as`)在默认导入之后
282
+ 3. 命名导入按照 `type` 类型优先,然后按最终导入名称字母顺序排序
283
+
284
+ ```typescript
285
+
286
+ ```
287
+
288
+ **自定义行为**:
289
+
290
+ 如果提供了自定义的 `sortImportContent` 函数,插件会**完全遵循你的排序逻辑**:
291
+
292
+ ```javascript
293
+ createPlugin({
294
+ // 完全按字母顺序,不区分 type 和 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
+ ### 导入语句排序
308
+
309
+ 导入语句按模块路径的字母顺序排序:
310
+
311
+ ```typescript
312
+
313
+ ```
314
+
315
+ ### 注释处理
316
+
317
+ 注释会跟随它们所附加的导入语句一起移动:
318
+
319
+ ```typescript
320
+
321
+ ```
322
+
323
+ ## 实现细节
324
+
325
+ ### 核心模块
326
+
327
+ #### 1. 类型定义 (`src/types.ts`)
328
+
329
+ 定义所有接口类型:ImportContent、ImportStatement、Group、PluginConfig 和各种函数类型。
330
+
331
+ #### 2. 解析器 (`src/parser.ts`)
332
+
333
+ 使用 `@babel/parser` 解析源代码,提取导入/导出语句:
334
+
335
+ - 解析源代码为 AST
336
+ - 遍历 AST 找到所有 import 和 export 语句
337
+ - 识别导入类型:默认导入、命名导入、命名空间导入、副作用导入
338
+ - 识别 TypeScript 的 `type` 导入标记
339
+ - 提取并保留导入语句上方的注释
340
+ - 记录导入语句的位置信息
341
+
342
+ #### 3. 排序器 (`src/sorter.ts`)
343
+
344
+ 实现分组和排序逻辑:
345
+
346
+ - 根据 `getGroup` 函数对导入语句进行分组
347
+ - 如果 `sortSideEffect` 为 false,将副作用导入作为分隔符处理
348
+ - 使用各种排序函数对分组、导入语句、导入内容进行排序
349
+ - 支持完全自定义的排序逻辑
350
+
351
+ #### 4. 格式化器 (`src/formatter.ts`)
352
+
353
+ 将排序后的导入语句转换回代码字符串:
354
+
355
+ - 根据 `ImportStatement` 生成对应的 import/export 代码
356
+ - 处理默认导入、命名导入、命名空间导入的格式
357
+ - 处理 `type` 导入的格式
358
+ - 根据 `separator` 配置在分组之间插入分隔符
359
+ - 保持注释关联
360
+
361
+ #### 5. 插件入口 (`src/index.ts`)
362
+
363
+ 实现 Prettier 插件标准接口:
364
+
365
+ - 扩展现有的 babel/typescript 解析器
366
+ - 支持工厂函数模式
367
+ - 集成解析器、排序器、格式化器
368
+ - 只处理文件开头的连续导入语句块
369
+
370
+ #### 6. 分析器 (`src/analyzer.ts`)
371
+
372
+ 分析代码中使用的标识符并过滤未使用的导入:
373
+
374
+ - 使用 `@babel/traverse` 遍历 AST
375
+ - 收集代码中使用的所有标识符(变量、函数、JSX 组件、类型引用等)
376
+ - 过滤导入语句,只保留在代码中使用的导入内容
377
+ - 支持识别别名、默认导入、命名空间导入等
378
+
379
+ ### 技术栈
380
+
381
+ - **构建工具**:rslib
382
+ - **解析器**:@babel/parser
383
+ - **AST 遍历**:@babel/traverse
384
+ - **AST 类型**:@babel/types
385
+ - **插件系统**:Prettier 3.x
386
+
387
+ ### 工厂函数模式的优势
388
+
389
+ Prettier 原生无法接受函数作为配置参数(因为配置需要序列化)。本插件通过工厂函数模式巧妙地解决了这个问题:
390
+
391
+ ```javascript
392
+ // 工厂函数在配置文件中被调用,返回一个插件实例
393
+ import { createPlugin } from "prettier-plugin-import-sorts"
394
+
395
+ export default {
396
+ plugins: [
397
+ createPlugin({
398
+ // 可以传递函数!
399
+ getGroup: statement => {
400
+ /* ... */
401
+ },
402
+ }),
403
+ ],
404
+ }
405
+ ```
406
+
407
+ 这样既保持了配置的灵活性,又不违反 Prettier 的配置系统限制。
408
+
409
+ ## 注意事项
410
+
411
+ 1. **只处理文件开头的连续导入/导出语句块**
412
+ - 遇到非导入/导出语句后,后续的导入不会被处理
413
+
414
+ 2. **支持的文件类型**
415
+ - JavaScript:`.js`, `.jsx`, `.mjs`, `.cjs`, `.mjsx`, `.cjsx`
416
+ - TypeScript:`.ts`, `.tsx`, `.mts`, `.cts`, `.mtsx`, `.ctsx`
417
+
418
+ 3. **不支持 CommonJS 的 `require` 语句**
419
+ - 只支持 ES6 模块语法(import/export)
420
+
421
+ 4. **自定义排序函数**
422
+ - 提供自定义 `sortImportContent` 时,插件会完全遵循你的逻辑
423
+ - 不会强制默认导入在前或 type 在前等规则
424
+
425
+ ## 项目状态
426
+
427
+ ✅ **完成并可用**
428
+
429
+ 所有核心功能已实现并通过测试,插件可以正常工作并集成到任何使用 Prettier 的项目中。
430
+
431
+ ### 已验证场景
432
+
433
+ 1. ✅ 基本导入排序(按字母顺序)
434
+ 2. ✅ 副作用导入作为分隔符
435
+ 3. ✅ 副作用导入排序(开启选项)
436
+ 4. ✅ 注释跟随导入语句
437
+ 5. ✅ TypeScript type 导入优先
438
+ 6. ✅ 默认导入和命名空间导入位置
439
+ 7. ✅ 混合导入(默认 + 命名)
440
+ 8. ✅ 导入内容按 alias 排序
441
+ 9. ✅ 自定义排序逻辑
442
+
443
+ ## 下一步(可选)
444
+
445
+ 1. 添加单元测试(使用 Jest 或 Vitest)
446
+ 2. 添加 CI/CD 配置
447
+ 3. 发布到 npm
448
+ 4. 添加更多示例
449
+ 5. 支持更多配置选项(如忽略特定导入)
450
+
451
+ ## License
452
+
453
+ MIT
@@ -0,0 +1,7 @@
1
+ import { ImportStatement } from "./types";
2
+ /** 分析代码中使用的标识符 */
3
+ export declare function analyzeUsedIdentifiers(code: string): Set<string>;
4
+ /** 过滤未使用的导入内容 */
5
+ export declare function filterUnusedImports(importStatement: ImportStatement, usedIdentifiers: Set<string>): ImportStatement;
6
+ /** 从导入语句列表中移除未使用的导入 */
7
+ export declare function removeUnusedImportsFromStatements(importStatements: ImportStatement[], code: string): ImportStatement[];
@@ -0,0 +1,7 @@
1
+ import { Group, ImportStatement, PluginConfig } from "./types";
2
+ /** 格式化导入语句 */
3
+ export declare function formatImportStatement(statement: ImportStatement): string;
4
+ /** 格式化分组 */
5
+ export declare function formatGroups(groups: Group[], config: PluginConfig): string;
6
+ /** 格式化导入语句列表(不使用分组) */
7
+ export declare function formatImportStatements(statements: ImportStatement[]): string;
@@ -0,0 +1,7 @@
1
+ import { Plugin } from "prettier";
2
+ import { PluginConfig } from "./types";
3
+ /** 默认插件实例(用于简单使用) */
4
+ declare const plugin: Plugin;
5
+ /** 创建自定义配置的插件(工厂函数) */
6
+ export declare function createPlugin(config?: PluginConfig): Plugin;
7
+ export default plugin;