@1adybug/prettier-plugin-sort-imports 0.0.6 → 0.0.8
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 +26 -0
- package/README.zh-CN.md +1 -0
- package/dist/index.js +60 -21
- package/dist/sorter.d.ts +1 -1
- package/dist/types.d.ts +2 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@ A powerful Prettier plugin for intelligently grouping and sorting import stateme
|
|
|
13
13
|
- ✅ **Side Effect Handling**: Configurable sorting behavior for side effect imports
|
|
14
14
|
- ✅ **Unused Import Removal**: Optional automatic removal of unused imports
|
|
15
15
|
- ✅ **Factory Function Pattern**: Support for custom functions in configuration files
|
|
16
|
+
- ✅ **Tailwind CSS Integration**: Compatible with `prettier-plugin-tailwindcss`
|
|
16
17
|
|
|
17
18
|
## Quick Start
|
|
18
19
|
|
|
@@ -288,6 +289,7 @@ export default {
|
|
|
288
289
|
```
|
|
289
290
|
|
|
290
291
|
**Benefits of this approach**:
|
|
292
|
+
|
|
291
293
|
- ✅ **Reusable**: Share the same configuration across multiple projects
|
|
292
294
|
- ✅ **Version Control**: Track your import sorting rules in git
|
|
293
295
|
- ✅ **Maintainable**: Keep complex logic separate from prettier config
|
|
@@ -556,6 +558,30 @@ export default {
|
|
|
556
558
|
|
|
557
559
|
This maintains configuration flexibility while not violating Prettier's configuration system limitations.
|
|
558
560
|
|
|
561
|
+
## Integration with Other Plugins
|
|
562
|
+
|
|
563
|
+
### Tailwind CSS
|
|
564
|
+
|
|
565
|
+
This plugin works seamlessly with `prettier-plugin-tailwindcss`. For detailed setup instructions, see [TAILWINDCSS_INTEGRATION.md](./TAILWINDCSS_INTEGRATION.md).
|
|
566
|
+
|
|
567
|
+
**Quick Setup:**
|
|
568
|
+
|
|
569
|
+
```javascript
|
|
570
|
+
// prettier.config.mjs
|
|
571
|
+
export default {
|
|
572
|
+
plugins: [
|
|
573
|
+
"@1adybug/prettier-plugin-sort-imports",
|
|
574
|
+
"prettier-plugin-tailwindcss", // Must come last
|
|
575
|
+
],
|
|
576
|
+
tailwindFunctions: ["clsx", "cn", "cva", "tw"],
|
|
577
|
+
}
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
This will:
|
|
581
|
+
|
|
582
|
+
- ✅ Sort and merge your imports
|
|
583
|
+
- ✅ Sort your Tailwind CSS classes according to the recommended order
|
|
584
|
+
|
|
559
585
|
## Notes
|
|
560
586
|
|
|
561
587
|
1. **Only processes consecutive import/export statement blocks at the beginning of files**
|
package/README.zh-CN.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -72,9 +72,13 @@ function removeUnusedImportsFromStatements(importStatements, code) {
|
|
|
72
72
|
return filteredStatements;
|
|
73
73
|
}
|
|
74
74
|
function formatImportStatement(statement) {
|
|
75
|
-
const { path, isExport, isSideEffect, importContents, leadingComments, trailingComments, removedTrailingComments } = statement;
|
|
75
|
+
const { path, isExport, isSideEffect, importContents, leadingComments, trailingComments, removedTrailingComments, emptyLinesAfterComments } = statement;
|
|
76
76
|
const lines = [];
|
|
77
|
-
if (leadingComments && leadingComments.length > 0)
|
|
77
|
+
if (leadingComments && leadingComments.length > 0) {
|
|
78
|
+
lines.push(...leadingComments);
|
|
79
|
+
const emptyLines = emptyLinesAfterComments ?? 0;
|
|
80
|
+
for(let i = 0; i < emptyLines; i++)lines.push("");
|
|
81
|
+
}
|
|
78
82
|
if (isSideEffect) {
|
|
79
83
|
let importLine = "";
|
|
80
84
|
importLine = isExport ? `export * from "${path}"` : `import "${path}"`;
|
|
@@ -170,30 +174,42 @@ function parseImports(code) {
|
|
|
170
174
|
const importStatements = [];
|
|
171
175
|
const { body } = ast.program;
|
|
172
176
|
const usedComments = new Set();
|
|
177
|
+
let isFirstImport = true;
|
|
173
178
|
for (const node of body)if ("ImportDeclaration" === node.type || "ExportNamedDeclaration" === node.type && node.source || "ExportAllDeclaration" === node.type) {
|
|
174
|
-
const statement = parseImportNode(node, ast.comments ?? [], usedComments);
|
|
179
|
+
const statement = parseImportNode(node, ast.comments ?? [], usedComments, code, isFirstImport);
|
|
175
180
|
importStatements.push(statement);
|
|
181
|
+
isFirstImport = false;
|
|
176
182
|
} else break;
|
|
177
183
|
return importStatements;
|
|
178
184
|
}
|
|
179
|
-
function parseImportNode(node, comments, usedComments) {
|
|
185
|
+
function parseImportNode(node, comments, usedComments, code, isFirstImport) {
|
|
180
186
|
node.type;
|
|
181
187
|
const source = node.source?.value ?? "";
|
|
182
|
-
node.loc?.start.line;
|
|
188
|
+
const nodeStartLine = node.loc?.start.line ?? 0;
|
|
183
189
|
node.loc?.end.line;
|
|
184
190
|
const nodeStart = node.start ?? 0;
|
|
185
191
|
let nodeEnd = node.end ?? 0;
|
|
186
192
|
const leadingComments = [];
|
|
187
193
|
const trailingComments = [];
|
|
188
194
|
let start = nodeStart;
|
|
195
|
+
let emptyLinesAfterComments = 0;
|
|
189
196
|
if (node.leadingComments) {
|
|
197
|
+
let lastCommentEndLine = 0;
|
|
190
198
|
for (const comment of node.leadingComments)if (!usedComments.has(comment)) {
|
|
199
|
+
const commentEndLine = comment.loc?.end.line ?? 0;
|
|
200
|
+
const emptyLinesBetween = nodeStartLine - commentEndLine - 1;
|
|
201
|
+
if (isFirstImport && emptyLinesBetween >= 1) {
|
|
202
|
+
usedComments.add(comment);
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
191
205
|
if ("CommentLine" === comment.type) leadingComments.push(`//${comment.value}`);
|
|
192
206
|
else if ("CommentBlock" === comment.type) leadingComments.push(`/*${comment.value}*/`);
|
|
193
207
|
const commentStart = comment.start ?? 0;
|
|
194
208
|
if (commentStart < start) start = commentStart;
|
|
209
|
+
lastCommentEndLine = commentEndLine;
|
|
195
210
|
usedComments.add(comment);
|
|
196
211
|
}
|
|
212
|
+
if (leadingComments.length > 0 && lastCommentEndLine > 0) emptyLinesAfterComments = nodeStartLine - lastCommentEndLine - 1;
|
|
197
213
|
}
|
|
198
214
|
if (node.trailingComments) {
|
|
199
215
|
for (const comment of node.trailingComments)if (!usedComments.has(comment)) {
|
|
@@ -221,6 +237,7 @@ function parseImportNode(node, comments, usedComments) {
|
|
|
221
237
|
importContents,
|
|
222
238
|
leadingComments: leadingComments.length > 0 ? leadingComments : void 0,
|
|
223
239
|
trailingComments: trailingComments.length > 0 ? trailingComments : void 0,
|
|
240
|
+
emptyLinesAfterComments: emptyLinesAfterComments > 0 ? emptyLinesAfterComments : void 0,
|
|
224
241
|
start,
|
|
225
242
|
end
|
|
226
243
|
};
|
|
@@ -228,10 +245,11 @@ function parseImportNode(node, comments, usedComments) {
|
|
|
228
245
|
if ("ExportAllDeclaration" === node.type) return {
|
|
229
246
|
path: source,
|
|
230
247
|
isExport: true,
|
|
231
|
-
isSideEffect:
|
|
248
|
+
isSideEffect: true,
|
|
232
249
|
importContents: [],
|
|
233
250
|
leadingComments: leadingComments.length > 0 ? leadingComments : void 0,
|
|
234
251
|
trailingComments: trailingComments.length > 0 ? trailingComments : void 0,
|
|
252
|
+
emptyLinesAfterComments: emptyLinesAfterComments > 0 ? emptyLinesAfterComments : void 0,
|
|
235
253
|
start,
|
|
236
254
|
end
|
|
237
255
|
};
|
|
@@ -244,6 +262,7 @@ function parseImportNode(node, comments, usedComments) {
|
|
|
244
262
|
importContents,
|
|
245
263
|
leadingComments: leadingComments.length > 0 ? leadingComments : void 0,
|
|
246
264
|
trailingComments: trailingComments.length > 0 ? trailingComments : void 0,
|
|
265
|
+
emptyLinesAfterComments: emptyLinesAfterComments > 0 ? emptyLinesAfterComments : void 0,
|
|
247
266
|
start,
|
|
248
267
|
end
|
|
249
268
|
};
|
|
@@ -581,11 +600,11 @@ function createCombinedPreprocess(parserName, config) {
|
|
|
581
600
|
...prettierOptions
|
|
582
601
|
};
|
|
583
602
|
const preprocessFunctions = [];
|
|
603
|
+
preprocessFunctions.push((text, options)=>preprocessImports(text, options, config));
|
|
584
604
|
for (const plugin of otherPlugins){
|
|
585
605
|
const parser = plugin?.parsers?.[parserName];
|
|
586
606
|
if (parser?.preprocess && "function" == typeof parser.preprocess) preprocessFunctions.push(parser.preprocess);
|
|
587
607
|
}
|
|
588
|
-
preprocessFunctions.push((text, options)=>preprocessImports(text, options, config));
|
|
589
608
|
let processedText = text;
|
|
590
609
|
for (const preprocess of preprocessFunctions)try {
|
|
591
610
|
processedText = preprocess(processedText, mergedOptions);
|
|
@@ -620,23 +639,43 @@ function createPluginInstance(config = {}) {
|
|
|
620
639
|
...baseOptions
|
|
621
640
|
};
|
|
622
641
|
for (const plugin of otherPlugins)if (plugin?.options) Object.assign(mergedOptions, plugin.options);
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
642
|
+
const mergedPrinters = {};
|
|
643
|
+
for (const plugin of otherPlugins)if (plugin?.printers) Object.assign(mergedPrinters, plugin.printers);
|
|
644
|
+
const mergedParsers = {};
|
|
645
|
+
const parserNames = [
|
|
646
|
+
"babel",
|
|
647
|
+
"typescript",
|
|
648
|
+
"babel-ts"
|
|
649
|
+
];
|
|
650
|
+
const baseParsers = {
|
|
651
|
+
babel,
|
|
652
|
+
typescript,
|
|
653
|
+
"babel-ts": babelTs
|
|
654
|
+
};
|
|
655
|
+
for (const parserName of parserNames){
|
|
656
|
+
const baseParser = baseParsers[parserName];
|
|
657
|
+
let merged = {
|
|
658
|
+
...baseParser
|
|
659
|
+
};
|
|
660
|
+
for (const plugin of otherPlugins){
|
|
661
|
+
const otherParser = plugin?.parsers?.[parserName];
|
|
662
|
+
if (otherParser) {
|
|
663
|
+
const { preprocess, ...otherAttrs } = otherParser;
|
|
664
|
+
merged = {
|
|
665
|
+
...merged,
|
|
666
|
+
...otherAttrs
|
|
667
|
+
};
|
|
636
668
|
}
|
|
637
|
-
}
|
|
669
|
+
}
|
|
670
|
+
merged.preprocess = createCombinedPreprocess(parserName, config);
|
|
671
|
+
mergedParsers[parserName] = merged;
|
|
672
|
+
}
|
|
673
|
+
const result = {
|
|
674
|
+
parsers: mergedParsers,
|
|
638
675
|
options: mergedOptions
|
|
639
676
|
};
|
|
677
|
+
if (Object.keys(mergedPrinters).length > 0) result.printers = mergedPrinters;
|
|
678
|
+
return result;
|
|
640
679
|
}
|
|
641
680
|
function createPlugin(config = {}) {
|
|
642
681
|
return createPluginInstance(config);
|
package/dist/sorter.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Group, ImportContent, ImportStatement, PluginConfig } from "./types";
|
|
1
|
+
import { Group, ImportContent, ImportStatement, PluginConfig } from "./types";
|
|
2
2
|
/** 合并后的配置 */
|
|
3
3
|
export interface MergedConfig extends Omit<Required<PluginConfig>, "separator" | "removeUnusedImports" | "otherPlugins" | "prettierOptions"> {
|
|
4
4
|
separator: PluginConfig["separator"];
|
package/dist/types.d.ts
CHANGED
|
@@ -28,6 +28,8 @@ export interface ImportStatement {
|
|
|
28
28
|
trailingComments?: string[];
|
|
29
29
|
/** 被移除的导入语句的行尾注释(合并时使用) */
|
|
30
30
|
removedTrailingComments?: string[];
|
|
31
|
+
/** 前导注释后的空行数(用于保留注释和 import 之间的空行) */
|
|
32
|
+
emptyLinesAfterComments?: number;
|
|
31
33
|
/** 在源代码中的起始位置(包括注释) */
|
|
32
34
|
start?: number;
|
|
33
35
|
/** 在源代码中的结束位置 */
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@1adybug/prettier-plugin-sort-imports",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.8",
|
|
5
5
|
"description": "一个 Prettier 插件,用于对 JavaScript/TypeScript 文件的导入语句进行分组和排序",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"prettier",
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"@types/bun": "latest",
|
|
55
55
|
"@types/node": "^22.18.6",
|
|
56
56
|
"prettier": "^3.6.2",
|
|
57
|
+
"prettier-plugin-tailwindcss": "^0.7.0",
|
|
57
58
|
"supports-color": "^10.2.2",
|
|
58
59
|
"typescript": "^5.9.2"
|
|
59
60
|
},
|