prettier-plugin-sort 0.0.3 → 0.1.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 CHANGED
@@ -56,7 +56,7 @@ import react from 'react';
56
56
  import App from './App.tsx';
57
57
  ```
58
58
 
59
- Each import belongs to a group. For example, `node:fs` is a `builtin` (covers Node.js / Bun / Deno built-ins), while `react` and `lodash` are `external` npm packages. The plugin first classifies each import by group, then sorts alphabetically within each group.
59
+ Each import belongs to a group. For example, `node:fs` is a `builtin` (covers Node.js and Bun **runtime** built-ins), while `react` and `lodash` are `external` npm packages. The plugin first classifies each import by group, then sorts alphabetically within each group.
60
60
 
61
61
  Grouping and ordering follow the conventions of [eslint-plugin-import](https://github.com/import-js/eslint-plugin-import)'s [import/order](https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/order.md) rule.
62
62
 
@@ -76,7 +76,7 @@ Group matchers:
76
76
 
77
77
  | Group | Matches | Examples |
78
78
  | ---------- | ----------------------------------------------------------- | ---------------------------------- |
79
- | `builtin` | `node:*`, `bun:*`, `deno:*`, and unprefixed Node built-ins | `node:fs`, `path` |
79
+ | `builtin` | `node:*`, `bun:*`, `bun`, and unprefixed Node built-ins | `node:fs`, `path`, `bun` |
80
80
  | `external` | npm packages, and anything that doesn't match another group | `react`, `@scope/pkg` |
81
81
  | `internal` | Project absolute paths and aliases | `/utils`, `~/app`, `@/shared` |
82
82
  | `parent` | Parent-relative paths | `../Button` |
package/README.zh.md CHANGED
@@ -56,7 +56,7 @@ import react from 'react';
56
56
  import App from './App.tsx';
57
57
  ```
58
58
 
59
- import 的模块一般可以按其来源分为不同的类别,例如上面的示例代码,'node:fs' 就属于 builtin 分类,该分类涵盖 Node.jsBun、Deno 等**运行时**的内置模块。而像 react 和 lodash 这种直接从 npm 下载的依赖模块,属于 external 分类。
59
+ import 的模块一般可以按其来源分为不同的类别,例如上面的示例代码,'node:fs' 就属于 builtin 分类,该分类涵盖 Node.jsBun **运行时**的内置模块。而像 react 和 lodash 这种直接从 npm 下载的依赖模块,属于 external 分类。
60
60
 
61
61
  插件会先根据不同的模块进行分类,然后在模块内部基于字母顺序进行排列。
62
62
 
@@ -76,14 +76,14 @@ import 的模块一般可以按其来源分为不同的类别,例如上面的
76
76
 
77
77
  各分组的匹配规则如下:
78
78
 
79
- | 分组 | 匹配内容 | 示例 |
80
- | ---------- | ------------------------------------------------------- | ---------------------------------- |
81
- | `builtin` | `node:*`、`bun:*`、`deno:*`,以及无前缀的 Node 内置模块 | `node:fs`、`path` |
82
- | `external` | npm 包,以及不属于其他分组的模块 | `react`、`@scope/pkg` |
83
- | `internal` | 项目绝对路径与别名 | `/utils`、`~/app`、`@/shared` |
84
- | `parent` | 向上跳级的相对路径 | `../Button` |
85
- | `sibling` | 同级相对路径(不包含 index) | `./Icon` |
86
- | `index` | 当前目录的 index 模块 | `.`、`./`、`./index`、`./index.ts` |
79
+ | 分组 | 匹配内容 | 示例 |
80
+ | ---------- | ---------------------------------------------------- | ---------------------------------- |
81
+ | `builtin` | `node:*`、`bun:*`、`bun`,以及无前缀的 Node 内置模块 | `node:fs`、`path`、`bun` |
82
+ | `external` | npm 包,以及不属于其他分组的模块 | `react`、`@scope/pkg` |
83
+ | `internal` | 项目绝对路径与别名 | `/utils`、`~/app`、`@/shared` |
84
+ | `parent` | 向上跳级的相对路径 | `../Button` |
85
+ | `sibling` | 同级相对路径(不包含 index) | `./Icon` |
86
+ | `index` | 当前目录的 index 模块 | `.`、`./`、`./index`、`./index.ts` |
87
87
 
88
88
  > **关于 `internal` 的检测方式:** 插件目前通过说明符的前缀硬编码(`/`、`~`、`@/`)来判断,不会读取 tsconfig `paths` 或任何构建工具的配置。后续版本可能会提供 `importOrderInternalPatterns` 选项,支持自定义正则匹配。
89
89
 
package/dist/index.js CHANGED
@@ -27,26 +27,24 @@ var VALID_TYPE_STYLES = new Set([
27
27
  "inline-last",
28
28
  "mixed"
29
29
  ]);
30
- var isValidImportGroup = (g) => typeof g === "string" && VALID_IMPORT_GROUPS.has(g);
31
- var isValidTypeStyle = (s) => typeof s === "string" && VALID_TYPE_STYLES.has(s);
30
+ var isValidImportGroup = (value) => typeof value === "string" && VALID_IMPORT_GROUPS.has(value);
31
+ var isValidTypeStyle = (value) => typeof value === "string" && VALID_TYPE_STYLES.has(value);
32
+ function resolveBoolean(rawOptions, key) {
33
+ const raw = rawOptions[key];
34
+ return typeof raw === "boolean" ? raw : DEFAULT_SORT_OPTIONS[key];
35
+ }
32
36
  function resolveSortOptions(rawOptions) {
33
37
  const groups = Array.isArray(rawOptions.importOrderGroups) ? rawOptions.importOrderGroups.filter(isValidImportGroup) : [];
34
- const excludeKeys = Array.isArray(rawOptions.packageJsonOrderExcludeKeys) ? rawOptions.packageJsonOrderExcludeKeys.filter((k) => typeof k === "string") : [];
35
- const importOrder = typeof rawOptions.importOrder === "boolean" ? rawOptions.importOrder : DEFAULT_SORT_OPTIONS.importOrder;
36
- const importOrderGroups = groups.length > 0 ? groups : [...DEFAULT_SORT_OPTIONS.importOrderGroups];
37
- const importOrderSeparation = typeof rawOptions.importOrderSeparation === "boolean" ? rawOptions.importOrderSeparation : DEFAULT_SORT_OPTIONS.importOrderSeparation;
38
+ const excludeKeys = Array.isArray(rawOptions.packageJsonOrderExcludeKeys) ? rawOptions.packageJsonOrderExcludeKeys.filter((key) => typeof key === "string") : [];
38
39
  const importOrderTypeImports = isValidTypeStyle(rawOptions.importOrderTypeImports) ? rawOptions.importOrderTypeImports : DEFAULT_SORT_OPTIONS.importOrderTypeImports;
39
- const importOrderMergeDuplicates = typeof rawOptions.importOrderMergeDuplicates === "boolean" ? rawOptions.importOrderMergeDuplicates : DEFAULT_SORT_OPTIONS.importOrderMergeDuplicates;
40
- const exportOrder = typeof rawOptions.exportOrder === "boolean" ? rawOptions.exportOrder : DEFAULT_SORT_OPTIONS.exportOrder;
41
- const packageJsonOrder = typeof rawOptions.packageJsonOrder === "boolean" ? rawOptions.packageJsonOrder : DEFAULT_SORT_OPTIONS.packageJsonOrder;
42
40
  return {
43
- importOrder,
44
- importOrderGroups,
45
- importOrderSeparation,
41
+ importOrder: resolveBoolean(rawOptions, "importOrder"),
42
+ importOrderGroups: groups.length > 0 ? groups : [...DEFAULT_SORT_OPTIONS.importOrderGroups],
43
+ importOrderSeparation: resolveBoolean(rawOptions, "importOrderSeparation"),
46
44
  importOrderTypeImports,
47
- importOrderMergeDuplicates,
48
- exportOrder,
49
- packageJsonOrder,
45
+ importOrderMergeDuplicates: resolveBoolean(rawOptions, "importOrderMergeDuplicates"),
46
+ exportOrder: resolveBoolean(rawOptions, "exportOrder"),
47
+ packageJsonOrder: resolveBoolean(rawOptions, "packageJsonOrder"),
50
48
  packageJsonOrderExcludeKeys: excludeKeys
51
49
  };
52
50
  }
@@ -123,26 +121,26 @@ var options = {
123
121
 
124
122
  // src/utils.ts
125
123
  function splitTopLevel(input, separator) {
126
- const out = [];
127
- let buf = "";
124
+ const segments = [];
125
+ let current = "";
128
126
  let depth = 0;
129
- for (const ch of input) {
130
- if (ch === "{" || ch === "(" || ch === "[") {
127
+ for (const char of input) {
128
+ if (char === "{" || char === "(" || char === "[") {
131
129
  depth++;
132
- } else if (ch === "}" || ch === ")" || ch === "]") {
130
+ } else if (char === "}" || char === ")" || char === "]") {
133
131
  depth--;
134
132
  }
135
- if (ch === separator && depth === 0) {
136
- out.push(buf);
137
- buf = "";
133
+ if (char === separator && depth === 0) {
134
+ segments.push(current);
135
+ current = "";
138
136
  continue;
139
137
  }
140
- buf += ch;
138
+ current += char;
141
139
  }
142
- if (buf.length > 0) {
143
- out.push(buf);
140
+ if (current.length > 0) {
141
+ segments.push(current);
144
142
  }
145
- return out.map((s) => s.trim()).filter((s) => s.length > 0);
143
+ return segments.map((segment) => segment.trim()).filter((segment) => segment.length > 0);
146
144
  }
147
145
 
148
146
  // src/sort-exports.ts
@@ -159,8 +157,8 @@ function sortExports(text, rawOptions) {
159
157
  const sorted = [...members].sort((a, b) => stripTypePrefix(a).localeCompare(stripTypePrefix(b), "en", {
160
158
  sensitivity: "base"
161
159
  }));
162
- const same = sorted.every((m, i) => m === members[i]);
163
- if (same) {
160
+ const unchanged = sorted.every((member, index) => member === members[index]);
161
+ if (unchanged) {
164
162
  return match;
165
163
  }
166
164
  const prefix = typeKeyword ? `export${typeKeyword}` : "export";
@@ -175,7 +173,7 @@ function stripTypePrefix(member) {
175
173
  import { builtinModules } from "node:module";
176
174
  var NODE_BUILTINS = new Set(builtinModules);
177
175
  function detectGroup(source) {
178
- if (source.startsWith("node:") || source.startsWith("bun:") || source.startsWith("deno:")) {
176
+ if (source === "bun" || source.startsWith("bun:") || source.startsWith("node:")) {
179
177
  return "builtin";
180
178
  }
181
179
  const slashIndex = source.indexOf("/");
@@ -197,20 +195,19 @@ function detectGroup(source) {
197
195
  }
198
196
  return "external";
199
197
  }
198
+ var TYPE_PREFIX = /^type\s+(.+)$/s;
200
199
  function splitMembers(inner) {
201
200
  return splitTopLevel(inner, ",").map((part) => {
202
- const isType = /^type\s+/.test(part);
203
- const name = isType ? part.replace(/^type\s+/, "").trim() : part;
204
- return { name, isType };
201
+ const match = TYPE_PREFIX.exec(part);
202
+ return match ? { name: match[1].trim(), isType: true } : { name: part, isType: false };
205
203
  });
206
204
  }
207
- function parseImport(stmt) {
208
- const trimmed = stmt.raw.trim();
209
- const leadingComments = stmt.leadingComments;
205
+ function parseImport(statement) {
206
+ const trimmed = statement.raw.trim();
207
+ const leadingComments = statement.leadingComments;
210
208
  const sideEffect = /^import\s*(['"])([^'"]+)\1(?:\s+with\s*(\{[^}]*\}))?\s*;?$/.exec(trimmed);
211
209
  if (sideEffect) {
212
210
  return {
213
- raw: trimmed,
214
211
  source: sideEffect[2] ?? "",
215
212
  typeClause: false,
216
213
  sideEffect: true,
@@ -221,14 +218,14 @@ function parseImport(stmt) {
221
218
  leadingComments
222
219
  };
223
220
  }
224
- const m = /^import\s+(type\s+)?([\s\S]+?)\s*from\s*(['"])([^'"]+)\3(?:\s+with\s*(\{[^}]*\}))?\s*;?$/.exec(trimmed);
225
- if (!m) {
221
+ const match = /^import\s+(type\s+)?([\s\S]+?)\s*from\s*(['"])([^'"]+)\3(?:\s+with\s*(\{[^}]*\}))?\s*;?$/.exec(trimmed);
222
+ if (!match) {
226
223
  return null;
227
224
  }
228
- const typeClause = Boolean(m[1]);
229
- const clause = (m[2] ?? "").trim();
230
- const source = m[4] ?? "";
231
- const attributes = m[5] ?? null;
225
+ const typeClause = Boolean(match[1]);
226
+ const clause = (match[2] ?? "").trim();
227
+ const source = match[4] ?? "";
228
+ const attributes = match[5] ?? null;
232
229
  let defaultSpec = null;
233
230
  let namespaceSpec = null;
234
231
  let members = null;
@@ -243,7 +240,6 @@ function parseImport(stmt) {
243
240
  }
244
241
  }
245
242
  return {
246
- raw: trimmed,
247
243
  source,
248
244
  typeClause,
249
245
  sideEffect: false,
@@ -255,7 +251,7 @@ function parseImport(stmt) {
255
251
  };
256
252
  }
257
253
  function extractImportBlock(text) {
258
- const firstRe = /(?:^|\n)(?:[ \t]*(?:\/\/[^\n]*|\/\*[\s\S]*?\*\/)[ \t]*\n)*[ \t]*import\b/;
254
+ const firstRe = /(?:^|\n)(?:[ \t]*(?:\/\/[^\n]*|\/\*[\s\S]*?\*\/)[ \t]*\n)*[ \t]*import\b(?![.(])/;
259
255
  const first = firstRe.exec(text);
260
256
  if (!first) {
261
257
  return null;
@@ -263,17 +259,21 @@ function extractImportBlock(text) {
263
259
  const start = first.index + (text[first.index] === `
264
260
  ` ? 1 : 0);
265
261
  const statements = [];
262
+ const skipRe = /(?:[ \t]*(?:\/\/[^\n]*|\/\*[\s\S]*?\*\/)?[ \t]*\n)*/y;
263
+ const importRe = /[ \t]*(import\b(?![.(])[\s\S]*?(?:from\s*(['"])[^'"]+\2|(['"])[^'"]+\3)(?:\s+with\s*\{[^}]*\})?\s*;?)/y;
266
264
  let cursor = start;
267
265
  while (cursor < text.length) {
268
- const chunkMatch = /^(?:[ \t]*(?:\/\/[^\n]*|\/\*[\s\S]*?\*\/)?[ \t]*\n)*/.exec(text.slice(cursor));
269
- const chunk = chunkMatch ? chunkMatch[0] : "";
270
- const afterSkip = cursor + chunk.length;
271
- const importMatch = /^[ \t]*(import\b[\s\S]*?(?:from\s*(['"])[^'"]+\2|(['"])[^'"]+\3)(?:\s+with\s*\{[^}]*\})?\s*;?)/.exec(text.slice(afterSkip));
266
+ skipRe.lastIndex = cursor;
267
+ const skipMatch = skipRe.exec(text);
268
+ const skipped = skipMatch ? skipMatch[0] : "";
269
+ const afterSkip = cursor + skipped.length;
270
+ importRe.lastIndex = afterSkip;
271
+ const importMatch = importRe.exec(text);
272
272
  if (!importMatch) {
273
273
  break;
274
274
  }
275
- const normalised = chunk.endsWith(`
276
- `) ? chunk.slice(0, -1) : chunk;
275
+ const normalised = skipped.endsWith(`
276
+ `) ? skipped.slice(0, -1) : skipped;
277
277
  const commentLines = normalised.length > 0 ? normalised.split(`
278
278
  `) : [];
279
279
  const leadingLines = [];
@@ -305,28 +305,23 @@ function extractImportBlock(text) {
305
305
  function renderMembers(members) {
306
306
  return members.map((member) => member.isType ? `type ${member.name}` : member.name).join(", ");
307
307
  }
308
+ function renderSpecifiers(importDecl) {
309
+ const parts = [];
310
+ if (importDecl.defaultSpec) {
311
+ parts.push(importDecl.defaultSpec);
312
+ }
313
+ if (importDecl.namespaceSpec) {
314
+ parts.push(importDecl.namespaceSpec);
315
+ }
316
+ if (importDecl.members) {
317
+ parts.push(`{ ${renderMembers(importDecl.members)} }`);
318
+ }
319
+ return parts.join(", ");
320
+ }
308
321
  function renderImport(importDecl) {
309
322
  const suffix = importDecl.attributes ? ` with ${importDecl.attributes}` : "";
310
- const body = (() => {
311
- if (importDecl.sideEffect) {
312
- return `import '${importDecl.source}'${suffix};`;
313
- }
314
- if (importDecl.typeClause) {
315
- const inner = importDecl.members ? `{ ${renderMembers(importDecl.members)} }` : "";
316
- return `import type ${inner} from '${importDecl.source}'${suffix};`;
317
- }
318
- const leftParts = [];
319
- if (importDecl.defaultSpec) {
320
- leftParts.push(importDecl.defaultSpec);
321
- }
322
- if (importDecl.namespaceSpec) {
323
- leftParts.push(importDecl.namespaceSpec);
324
- }
325
- if (importDecl.members) {
326
- leftParts.push(`{ ${renderMembers(importDecl.members)} }`);
327
- }
328
- return `import ${leftParts.join(", ")} from '${importDecl.source}'${suffix};`;
329
- })();
323
+ const source = `'${importDecl.source}'`;
324
+ const body = importDecl.sideEffect ? `import ${source}${suffix};` : importDecl.typeClause ? `import type ${renderSpecifiers(importDecl)} from ${source}${suffix};` : `import ${renderSpecifiers(importDecl)} from ${source}${suffix};`;
330
325
  return importDecl.leadingComments + body;
331
326
  }
332
327
  function sortMembersAlpha(members) {
@@ -343,30 +338,30 @@ function normalizeTypeClause(importDecl) {
343
338
  };
344
339
  }
345
340
  function mergeImportsFromSameSource(imports) {
346
- const indexBySource = new Map;
341
+ const indexByKey = new Map;
347
342
  const result = [];
348
- for (const raw of imports) {
349
- const importDecl = normalizeTypeClause(raw);
350
- if (importDecl.sideEffect) {
343
+ for (const rawImport of imports) {
344
+ const importDecl = normalizeTypeClause(rawImport);
345
+ if (importDecl.sideEffect || importDecl.typeClause) {
351
346
  result.push(importDecl);
352
347
  continue;
353
348
  }
354
- const existingIndex = indexBySource.get(importDecl.source);
349
+ const mergeKey = `${importDecl.source}\x00${importDecl.attributes ?? ""}`;
350
+ const existingIndex = indexByKey.get(mergeKey);
355
351
  if (existingIndex === undefined) {
356
- indexBySource.set(importDecl.source, result.length);
352
+ indexByKey.set(mergeKey, result.length);
357
353
  result.push(importDecl);
358
354
  continue;
359
355
  }
360
356
  const existing = result[existingIndex];
361
357
  result[existingIndex] = {
362
- raw: "",
363
358
  source: existing.source,
364
359
  typeClause: false,
365
360
  sideEffect: false,
366
361
  defaultSpec: existing.defaultSpec ?? importDecl.defaultSpec,
367
362
  namespaceSpec: existing.namespaceSpec ?? importDecl.namespaceSpec,
368
363
  members: existing.members === null && importDecl.members === null ? null : [...existing.members ?? [], ...importDecl.members ?? []],
369
- attributes: existing.attributes ?? importDecl.attributes,
364
+ attributes: existing.attributes,
370
365
  leadingComments: existing.leadingComments
371
366
  };
372
367
  }
@@ -385,7 +380,6 @@ function applyTypeImports(importDecl, style) {
385
380
  const out = [];
386
381
  if (typeMembers2.length > 0) {
387
382
  out.push({
388
- raw: "",
389
383
  source: importDecl.source,
390
384
  typeClause: true,
391
385
  sideEffect: false,
@@ -406,7 +400,7 @@ function applyTypeImports(importDecl, style) {
406
400
  }
407
401
  return out.length > 0 ? out : [importDecl];
408
402
  }
409
- const base = importDecl.typeClause ? {
403
+ const inlineBase = importDecl.typeClause ? {
410
404
  ...importDecl,
411
405
  typeClause: false,
412
406
  members: importDecl.members.map((member) => ({
@@ -415,14 +409,14 @@ function applyTypeImports(importDecl, style) {
415
409
  }))
416
410
  } : { ...importDecl, members: importDecl.members };
417
411
  if (style === "mixed") {
418
- return [{ ...base, members: sortMembersAlpha(base.members) }];
412
+ return [{ ...inlineBase, members: sortMembersAlpha(inlineBase.members) }];
419
413
  }
420
- const typeMembers = base.members.filter((m) => m.isType);
421
- const valueMembers = base.members.filter((m) => !m.isType);
414
+ const typeMembers = inlineBase.members.filter((member) => member.isType);
415
+ const valueMembers = inlineBase.members.filter((member) => !member.isType);
422
416
  const sortedTypes = sortMembersAlpha(typeMembers);
423
417
  const sortedValues = sortMembersAlpha(valueMembers);
424
418
  const ordered = style === "inline-first" ? [...sortedTypes, ...sortedValues] : [...sortedValues, ...sortedTypes];
425
- return [{ ...base, members: ordered }];
419
+ return [{ ...inlineBase, members: ordered }];
426
420
  }
427
421
  function sortSegment(imports, options2, groupIndex, fallback) {
428
422
  if (imports.length === 0) {
@@ -432,7 +426,7 @@ function sortSegment(imports, options2, groupIndex, fallback) {
432
426
  const deduped = options2.importOrderMergeDuplicates ? mergeImportsFromSameSource(imports) : imports;
433
427
  const rewritten = deduped.flatMap((importDecl) => applyTypeImports(importDecl, style));
434
428
  const decorated = rewritten.map((importDecl, index) => ({
435
- stmt: importDecl,
429
+ importDecl,
436
430
  group: detectGroup(importDecl.source),
437
431
  originalIndex: index
438
432
  }));
@@ -442,24 +436,24 @@ function sortSegment(imports, options2, groupIndex, fallback) {
442
436
  if (groupOrderA !== groupOrderB) {
443
437
  return groupOrderA - groupOrderB;
444
438
  }
445
- const sourceA = a.stmt.source.toLowerCase();
446
- const sourceB = b.stmt.source.toLowerCase();
439
+ const sourceA = a.importDecl.source.toLowerCase();
440
+ const sourceB = b.importDecl.source.toLowerCase();
447
441
  if (sourceA !== sourceB) {
448
442
  return sourceA < sourceB ? -1 : 1;
449
443
  }
450
- if (a.stmt.typeClause !== b.stmt.typeClause) {
451
- return a.stmt.typeClause ? -1 : 1;
444
+ if (a.importDecl.typeClause !== b.importDecl.typeClause) {
445
+ return a.importDecl.typeClause ? -1 : 1;
452
446
  }
453
447
  return a.originalIndex - b.originalIndex;
454
448
  });
455
449
  const lines = [];
456
- let prevGroup = null;
450
+ let previousGroup = null;
457
451
  for (const item of decorated) {
458
- if (options2.importOrderSeparation && prevGroup !== null && item.group !== prevGroup) {
452
+ if (options2.importOrderSeparation && previousGroup !== null && item.group !== previousGroup) {
459
453
  lines.push("");
460
454
  }
461
- lines.push(renderImport(item.stmt));
462
- prevGroup = item.group;
455
+ lines.push(renderImport(item.importDecl));
456
+ previousGroup = item.group;
463
457
  }
464
458
  return lines;
465
459
  }
@@ -472,7 +466,7 @@ function sortImports(text, rawOptions) {
472
466
  if (!block || block.statements.length === 0) {
473
467
  return text;
474
468
  }
475
- const parsed = block.statements.map((rawStmt) => parseImport(rawStmt)).filter((decl) => decl !== null);
469
+ const parsed = block.statements.map((rawStatement) => parseImport(rawStatement)).filter((importDecl) => importDecl !== null);
476
470
  if (parsed.length === 0) {
477
471
  return text;
478
472
  }
@@ -485,31 +479,30 @@ function sortImports(text, rawOptions) {
485
479
  let currentSegment = [];
486
480
  for (const importDecl of parsed) {
487
481
  if (importDecl.sideEffect) {
488
- chunks.push({ kind: "segment", imports: currentSegment });
489
- chunks.push({ kind: "side-effect", stmt: importDecl });
490
- currentSegment = [];
482
+ if (currentSegment.length > 0) {
483
+ chunks.push({ kind: "segment", imports: currentSegment });
484
+ currentSegment = [];
485
+ }
486
+ chunks.push({ kind: "side-effect", importDecl });
491
487
  } else {
492
488
  currentSegment.push(importDecl);
493
489
  }
494
490
  }
495
- chunks.push({ kind: "segment", imports: currentSegment });
491
+ if (currentSegment.length > 0) {
492
+ chunks.push({ kind: "segment", imports: currentSegment });
493
+ }
496
494
  const allLines = [];
495
+ let previousKind = null;
497
496
  for (const chunk of chunks) {
497
+ if (previousKind !== null && previousKind !== chunk.kind && options2.importOrderSeparation) {
498
+ allLines.push("");
499
+ }
498
500
  if (chunk.kind === "segment") {
499
- if (chunk.imports.length === 0) {
500
- continue;
501
- }
502
- const segmentLines = sortSegment(chunk.imports, options2, groupIndex, fallback);
503
- if (allLines.length > 0 && options2.importOrderSeparation) {
504
- allLines.push("");
505
- }
506
- allLines.push(...segmentLines);
501
+ allLines.push(...sortSegment(chunk.imports, options2, groupIndex, fallback));
507
502
  } else {
508
- if (allLines.length > 0 && options2.importOrderSeparation) {
509
- allLines.push("");
510
- }
511
- allLines.push(renderImport(chunk.stmt));
503
+ allLines.push(renderImport(chunk.importDecl));
512
504
  }
505
+ previousKind = chunk.kind;
513
506
  }
514
507
  const replacement = allLines.join(`
515
508
  `);
@@ -666,15 +659,15 @@ function sortObjectKeysByOrder(record, order) {
666
659
  rest.push(entry);
667
660
  }
668
661
  }
669
- known.sort(([left], [right]) => (orderIndex.get(left) ?? 0) - (orderIndex.get(right) ?? 0));
670
- rest.sort(([left], [right]) => left.localeCompare(right, "en"));
662
+ known.sort(([a], [b]) => (orderIndex.get(a) ?? 0) - (orderIndex.get(b) ?? 0));
663
+ rest.sort(([a], [b]) => a.localeCompare(b, "en"));
671
664
  return Object.fromEntries([...known, ...rest]);
672
665
  }
673
666
  function sortObjectKeysAlpha(value) {
674
667
  if (!isPlainObject(value)) {
675
668
  return value;
676
669
  }
677
- return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right, "en")));
670
+ return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b, "en")));
678
671
  }
679
672
  function sortStringArrayAlpha(value) {
680
673
  return [...value].sort((a, b) => a.localeCompare(b, "en"));
@@ -704,7 +697,7 @@ function sortPackageJson(text, rawOptions) {
704
697
  if (!isPlainObject(parsed)) {
705
698
  return text;
706
699
  }
707
- let result = { ...parsed };
700
+ let result = parsed;
708
701
  for (const field of DEPENDENCY_FIELDS) {
709
702
  const dependencyMap = result[field];
710
703
  if (dependencyMap !== undefined && !exclude.has(field)) {
@@ -735,8 +728,8 @@ function wrap(parser, ...transforms) {
735
728
  ...parser,
736
729
  async preprocess(text, parserOptions) {
737
730
  let source = parser.preprocess ? await parser.preprocess(text, parserOptions) : text;
738
- for (const fn of transforms) {
739
- source = fn(source, parserOptions);
731
+ for (const transform of transforms) {
732
+ source = transform(source, parserOptions);
740
733
  }
741
734
  return source;
742
735
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prettier-plugin-sort",
3
- "version": "0.0.3",
3
+ "version": "0.1.1",
4
4
  "description": "An all-in-one Prettier plugin to sort imports, package.json keys, and more.",
5
5
  "keywords": [
6
6
  "imports",