@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 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
@@ -282,6 +282,7 @@ export default {
282
282
  ```
283
283
 
284
284
  **此方法的优点**:
285
+
285
286
  - ✅ **可复用**:在多个项目间共享相同配置
286
287
  - ✅ **版本控制**:在 git 中跟踪你的导入排序规则
287
288
  - ✅ **易维护**:将复杂逻辑从 prettier 配置中分离
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) lines.push(...leadingComments);
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: false,
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
- return {
624
- parsers: {
625
- babel: {
626
- ...babel,
627
- preprocess: createCombinedPreprocess("babel", config)
628
- },
629
- typescript: {
630
- ...typescript,
631
- preprocess: createCombinedPreprocess("typescript", config)
632
- },
633
- "babel-ts": {
634
- ...babelTs,
635
- preprocess: createCombinedPreprocess("babel-ts", config)
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.6",
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
  },