@1adybug/prettier-plugin-sort-imports 0.0.26 → 0.0.28

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
@@ -152,6 +152,8 @@ interface PluginConfig {
152
152
  sortSideEffect?: boolean
153
153
  /** Whether to remove unused imports, defaults to false */
154
154
  removeUnusedImports?: boolean
155
+ /** Whether to add/remove the node: prefix for Node.js builtin modules */
156
+ nodeProtocol?: boolean
155
157
  }
156
158
  ```
157
159
 
@@ -167,6 +169,7 @@ export default {
167
169
  sortSideEffect: false, // Whether to sort side effect imports
168
170
  groupSeparator: "", // Group separator
169
171
  removeUnusedImports: false, // Whether to remove unused imports
172
+ nodeProtocol: true, // Add node: prefix for Node.js builtin modules (false to remove)
170
173
  }
171
174
  ```
172
175
 
@@ -198,6 +201,7 @@ export default {
198
201
  groupSeparator: "\n",
199
202
  sortSideEffect: true,
200
203
  removeUnusedImports: false,
204
+ nodeProtocol: true,
201
205
  }),
202
206
  ],
203
207
  }
@@ -347,7 +351,8 @@ Whether to remove unused imports, defaults to `false`.
347
351
 
348
352
  ```tsx
349
353
  // Before sorting
350
- import { useState } from "react"
354
+ // After sorting (with removeUnusedImports enabled)
355
+ import React, { useState } from "react"
351
356
 
352
357
  import { Button } from "antd"
353
358
 
@@ -356,10 +361,6 @@ function MyComponent() {
356
361
  return <Button>Click me</Button>
357
362
  }
358
363
 
359
- // After sorting (with removeUnusedImports enabled)
360
- import React, { useState } from "react"
361
- import { Button } from "antd"
362
-
363
364
  function MyComponent() {
364
365
  const [count, setCount] = useState(0)
365
366
  return <Button>Click me</Button>
@@ -373,6 +374,23 @@ function MyComponent() {
373
374
  - Analysis is AST-based and identifies actually used identifiers in code
374
375
  - Supports identifying JSX components, TypeScript type references, etc.
375
376
 
377
+ ### nodeProtocol
378
+
379
+ Whether to add/remove the `node:` prefix for Node.js builtin modules. Defaults to `undefined` (no change).
380
+
381
+ - `true`: add `node:` prefix
382
+ - `false`: remove `node:` prefix
383
+
384
+ ```ts
385
+ // Before
386
+ // nodeProtocol: false
387
+ import fs from "fs"
388
+ // nodeProtocol: true
389
+ import fs from "node:fs"
390
+ import path from "node:path"
391
+ import path from "path"
392
+ ```
393
+
376
394
  ### sortSideEffect
377
395
 
378
396
  Whether to sort side effect imports, defaults to `false`.
package/README.zh-CN.md CHANGED
@@ -151,6 +151,8 @@ interface PluginConfig {
151
151
  sortSideEffect?: boolean
152
152
  /** 是否删除未使用的导入,默认为 false */
153
153
  removeUnusedImports?: boolean
154
+ /** 是否为 Node.js 内置模块自动添加/移除 node: 前缀 */
155
+ nodeProtocol?: boolean
154
156
  }
155
157
  ```
156
158
 
@@ -166,6 +168,7 @@ export default {
166
168
  sortSideEffect: false, // 是否对副作用导入排序
167
169
  groupSeparator: "", // 分组分隔符
168
170
  removeUnusedImports: false, // 是否删除未使用的导入
171
+ nodeProtocol: true, // 为 Node.js 内置模块添加 node: 前缀(false 为移除)
169
172
  }
170
173
  ```
171
174
 
@@ -194,6 +197,7 @@ export default {
194
197
  groupSeparator: "",
195
198
  sortSideEffect: true,
196
199
  removeUnusedImports: false,
200
+ nodeProtocol: true,
197
201
  }),
198
202
  ],
199
203
  }
@@ -343,7 +347,8 @@ export default {
343
347
 
344
348
  ```tsx
345
349
  // 排序前
346
- import { useState } from "react"
350
+ // 排序后(开启 removeUnusedImports)
351
+ import React, { useState } from "react"
347
352
 
348
353
  import { Button } from "antd"
349
354
 
@@ -352,10 +357,6 @@ function MyComponent() {
352
357
  return <Button>Click me</Button>
353
358
  }
354
359
 
355
- // 排序后(开启 removeUnusedImports)
356
- import React, { useState } from "react"
357
- import { Button } from "antd"
358
-
359
360
  function MyComponent() {
360
361
  const [count, setCount] = useState(0)
361
362
  return <Button>Click me</Button>
@@ -369,6 +370,23 @@ function MyComponent() {
369
370
  - 分析基于 AST,会识别代码中实际使用的标识符
370
371
  - 支持识别 JSX 组件、TypeScript 类型引用等
371
372
 
373
+ ### nodeProtocol
374
+
375
+ 是否为 Node.js 内置模块自动添加/移除 `node:` 前缀,默认为 `undefined`(不处理)。
376
+
377
+ - `true`:自动添加 `node:` 前缀
378
+ - `false`:自动移除 `node:` 前缀
379
+
380
+ ```ts
381
+ // 排序前
382
+ // nodeProtocol: false
383
+ import fs from "fs"
384
+ // nodeProtocol: true
385
+ import fs from "node:fs"
386
+ import path from "node:path"
387
+ import path from "path"
388
+ ```
389
+
372
390
  ### sortSideEffect
373
391
 
374
392
  是否对副作用导入进行排序,默认为 `false`。
package/dist/index.d.ts CHANGED
@@ -17,6 +17,12 @@ export interface Options extends PrettierOptions {
17
17
  * @default false
18
18
  */
19
19
  removeUnusedImports?: boolean;
20
+ /**
21
+ * Whether to add/remove the node: prefix for Node.js builtin modules.
22
+ * true: add, false: remove, undefined: no change.
23
+ * @default undefined
24
+ */
25
+ nodeProtocol?: boolean;
20
26
  /**
21
27
  * 自定义获取分组名称。
22
28
  */
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createRequire } from "module";
1
+ import { builtinModules, createRequire } from "module";
2
2
  import { format } from "prettier";
3
3
  import { parse as parser_parse } from "@babel/parser";
4
4
  import traverse from "@babel/traverse";
@@ -209,7 +209,7 @@ function parseImports(code, filepath) {
209
209
  const statement = parseImportNode(node, ast.comments ?? [], usedComments, code, isFirstImport, filepath);
210
210
  importStatements.push(statement);
211
211
  isFirstImport = false;
212
- } else break;
212
+ }
213
213
  return importStatements;
214
214
  }
215
215
  function parseImportNode(node, comments, usedComments, code, isFirstImport, filepath) {
@@ -419,7 +419,8 @@ function mergeConfig(userConfig) {
419
419
  sortImportContent: userConfig.sortImportContent ?? DEFAULT_CONFIG.sortImportContent,
420
420
  groupSeparator: userConfig.groupSeparator,
421
421
  sortSideEffect: userConfig.sortSideEffect ?? DEFAULT_CONFIG.sortSideEffect,
422
- removeUnusedImports: userConfig.removeUnusedImports ?? false
422
+ removeUnusedImports: userConfig.removeUnusedImports ?? false,
423
+ nodeProtocol: userConfig.nodeProtocol
423
424
  };
424
425
  }
425
426
  function sortImports(imports, userConfig) {
@@ -583,6 +584,51 @@ function mergeImports(imports) {
583
584
  return Array.from(mergedMap.values());
584
585
  }
585
586
  const src_require = createRequire(import.meta.url);
587
+ const NODE_BUILTIN_MODULES = new Set(builtinModules.map((moduleName)=>moduleName.startsWith("node:") ? moduleName.slice(5) : moduleName));
588
+ function isNodeBuiltinModule(modulePath) {
589
+ const normalizedPath = modulePath.startsWith("node:") ? modulePath.slice(5) : modulePath;
590
+ if (NODE_BUILTIN_MODULES.has(normalizedPath)) return true;
591
+ const slashIndex = normalizedPath.indexOf("/");
592
+ if (-1 === slashIndex) return false;
593
+ return NODE_BUILTIN_MODULES.has(normalizedPath.slice(0, slashIndex));
594
+ }
595
+ function applyNodeProtocol(modulePath, nodeProtocol) {
596
+ if (void 0 === nodeProtocol) return modulePath;
597
+ const hasNodePrefix = modulePath.startsWith("node:");
598
+ const normalizedPath = hasNodePrefix ? modulePath.slice(5) : modulePath;
599
+ if (!isNodeBuiltinModule(normalizedPath)) return modulePath;
600
+ if (nodeProtocol) return hasNodePrefix ? modulePath : `node:${modulePath}`;
601
+ return hasNodePrefix ? normalizedPath : modulePath;
602
+ }
603
+ function getImportRanges(imports) {
604
+ const ranges = imports.map((statement)=>({
605
+ start: statement.start ?? 0,
606
+ end: statement.end ?? 0
607
+ })).filter((range)=>range.end > range.start).sort((a, b)=>a.start - b.start);
608
+ const merged = [];
609
+ for (const range of ranges){
610
+ const last = merged[merged.length - 1];
611
+ if (last && range.start <= last.end) {
612
+ last.end = Math.max(last.end, range.end);
613
+ continue;
614
+ }
615
+ merged.push({
616
+ ...range
617
+ });
618
+ }
619
+ return merged;
620
+ }
621
+ function removeRangesFromText(text, ranges) {
622
+ if (0 === ranges.length) return text;
623
+ let result = "";
624
+ let cursor = 0;
625
+ for (const range of ranges){
626
+ result += text.slice(cursor, range.start);
627
+ cursor = range.end;
628
+ }
629
+ result += text.slice(cursor);
630
+ return result;
631
+ }
586
632
  function preprocessImports(text, options, config = {}) {
587
633
  try {
588
634
  const parser = options.parser;
@@ -603,14 +649,17 @@ function preprocessImports(text, options, config = {}) {
603
649
  sortImportContent: config.sortImportContent ?? optionsConfig.sortImportContent,
604
650
  groupSeparator: config.groupSeparator ?? optionsConfig.groupSeparator,
605
651
  sortSideEffect: config.sortSideEffect ?? optionsConfig.sortSideEffect ?? false,
606
- removeUnusedImports: config.removeUnusedImports ?? optionsConfig.removeUnusedImports ?? false
652
+ removeUnusedImports: config.removeUnusedImports ?? optionsConfig.removeUnusedImports ?? false,
653
+ nodeProtocol: config.nodeProtocol ?? optionsConfig.nodeProtocol
607
654
  };
655
+ const importRanges = getImportRanges(imports);
656
+ const textWithoutImports = removeRangesFromText(text, importRanges);
608
657
  let processedImports = imports;
609
- if (finalConfig.removeUnusedImports) {
610
- const lastImport = imports[imports.length - 1];
611
- const codeAfterImports = text.slice(lastImport.end ?? 0);
612
- processedImports = removeUnusedImportsFromStatements(imports, codeAfterImports);
613
- }
658
+ if (finalConfig.removeUnusedImports) processedImports = removeUnusedImportsFromStatements(imports, textWithoutImports);
659
+ if (void 0 !== finalConfig.nodeProtocol) processedImports = processedImports.map((statement)=>({
660
+ ...statement,
661
+ path: applyNodeProtocol(statement.path, finalConfig.nodeProtocol)
662
+ }));
614
663
  const sortedImports = sortImports(processedImports, finalConfig);
615
664
  const mergedImports = mergeImports(sortedImports);
616
665
  let formattedImports;
@@ -620,13 +669,12 @@ function preprocessImports(text, options, config = {}) {
620
669
  formattedImports = formatGroups(sortedGroups, finalConfig, options.trailingComma);
621
670
  } else formattedImports = formatImportStatements(mergedImports, options.trailingComma);
622
671
  const firstImport = imports[0];
623
- const lastImport = imports[imports.length - 1];
624
672
  const startIndex = firstImport.start ?? 0;
625
- const endIndex = lastImport.end ?? text.length;
626
- const beforeImports = text.slice(0, startIndex);
627
- const afterImports = text.slice(endIndex);
673
+ const beforeImports = textWithoutImports.slice(0, startIndex);
674
+ let afterImports = textWithoutImports.slice(startIndex);
675
+ if (afterImports) afterImports = afterImports.replace(/^\n+/, "\n");
628
676
  const needsExtraNewline = afterImports && !afterImports.startsWith("\n");
629
- const separator = needsExtraNewline ? "\n\n" : "\n";
677
+ const separator = afterImports ? needsExtraNewline ? "\n\n" : "\n" : "";
630
678
  return beforeImports + formattedImports + separator + afterImports;
631
679
  } catch (error) {
632
680
  return text;
@@ -676,6 +724,11 @@ function createPluginInstance(config = {}) {
676
724
  category: "Import Sort",
677
725
  description: "�Ƿ�ɾ��δʹ�õĵ���",
678
726
  default: false
727
+ },
728
+ nodeProtocol: {
729
+ type: "boolean",
730
+ category: "Import Sort",
731
+ description: "Use node: prefix for Node.js builtin modules"
679
732
  }
680
733
  };
681
734
  const otherPlugins = config.otherPlugins || [];
package/dist/sorter.d.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import { Group, ImportContent, ImportStatement, PluginConfig } from "./types";
2
2
  /** 合并后的配置 */
3
- export interface MergedConfig extends Omit<Required<PluginConfig>, "groupSeparator" | "removeUnusedImports" | "otherPlugins" | "prettierOptions"> {
3
+ export interface MergedConfig extends Omit<Required<PluginConfig>, "groupSeparator" | "removeUnusedImports" | "nodeProtocol" | "otherPlugins" | "prettierOptions"> {
4
4
  groupSeparator: PluginConfig["groupSeparator"];
5
5
  removeUnusedImports: boolean;
6
+ nodeProtocol?: boolean;
6
7
  }
7
8
  /** 对导入语句进行排序 */
8
9
  export declare function sortImports(imports: ImportStatement[], userConfig: PluginConfig): ImportStatement[];
package/dist/types.d.ts CHANGED
@@ -76,6 +76,8 @@ export interface PluginConfig {
76
76
  sortSideEffect?: boolean;
77
77
  /** 是否删除未使用的导入,默认为 false */
78
78
  removeUnusedImports?: boolean;
79
+ /** Whether to add/remove the node: prefix for Node.js builtin modules */
80
+ nodeProtocol?: boolean;
79
81
  /** 要合并的其他 Prettier 插件,按传入顺序执行 */
80
82
  otherPlugins?: Plugin[];
81
83
  /** 传递给其他插件的 Prettier 配置选项 */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1adybug/prettier-plugin-sort-imports",
3
- "version": "0.0.26",
3
+ "version": "0.0.28",
4
4
  "description": "一个 Prettier 插件,用于对 JavaScript/TypeScript 文件的导入语句进行分组和排序",
5
5
  "keywords": [
6
6
  "prettier",
@@ -41,23 +41,23 @@
41
41
  "registry": "https://registry.npmjs.com/"
42
42
  },
43
43
  "dependencies": {
44
- "@babel/core": "^7.28.4",
45
- "@babel/parser": "^7.28.4",
46
- "@babel/traverse": "^7.28.5",
47
- "@babel/types": "^7.28.4"
44
+ "@babel/core": "^7.28.6",
45
+ "@babel/parser": "^7.28.6",
46
+ "@babel/traverse": "^7.28.6",
47
+ "@babel/types": "^7.28.6"
48
48
  },
49
49
  "devDependencies": {
50
- "@rslib/core": "^0.15.0",
50
+ "@rslib/core": "^0.15.1",
51
51
  "@types/babel__core": "^7.20.5",
52
52
  "@types/babel__traverse": "^7.28.0",
53
53
  "@types/bun": "latest",
54
- "@types/node": "^22.18.6",
54
+ "@types/node": "^24.10.8",
55
55
  "json5": "^2.2.3",
56
56
  "supports-color": "^10.2.2",
57
- "typescript": "^5.9.2"
57
+ "typescript": "^5.9.3"
58
58
  },
59
59
  "peerDependencies": {
60
- "prettier": "^3.7.3"
60
+ "prettier": "^3.8.0"
61
61
  },
62
62
  "scripts": {
63
63
  "build": "rslib build",