prettier-plugin-sort 0.0.2 → 0.1.0

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
@@ -182,6 +182,35 @@ import { useEffect, useState } from 'react';
182
182
 
183
183
  Set `importOrderMergeDuplicates` to `false` if you want to keep the original separate statements. Side-effect imports (`import 'mod';`) are never merged because their order has runtime semantics.
184
184
 
185
+ #### Side-effect imports
186
+
187
+ The order of side-effect imports (`import 'mod'`) often carries runtime meaning, such as CSS cascade order or polyfills that must load before a framework. The plugin never moves other imports across a side-effect import — imports on each side are sorted independently, and the side-effect import itself stays in place.
188
+
189
+ Before:
190
+
191
+ <!-- prettier-ignore -->
192
+ ```typescript
193
+ import Button from './Button';
194
+ import App from './App';
195
+ import 'normalize.css';
196
+ import theme from './theme';
197
+ import Icon from './Icon';
198
+ ```
199
+
200
+ After:
201
+
202
+ ```typescript
203
+ import App from './App';
204
+ import Button from './Button';
205
+
206
+ import 'normalize.css';
207
+
208
+ import Icon from './Icon';
209
+ import theme from './theme';
210
+ ```
211
+
212
+ Imports on each side are sorted independently. The side-effect import itself stays in place.
213
+
185
214
  Sorting rules:
186
215
 
187
216
  - Imports are classified into groups. Within each group they are sorted alphabetically
@@ -189,7 +218,7 @@ Sorting rules:
189
218
  - A blank line is inserted between groups by default. Disable with `importOrderSeparation`
190
219
  - `type` imports are split into their own statement by default. Use `importOrderTypeImports` to inline them instead
191
220
  - Multiple imports from the same source are merged into one by default. Disable with `importOrderMergeDuplicates`
192
- - Side-effect imports (`import 'mod'`) are never merged or moved across groups
221
+ - Side-effect imports (`import 'mod'`) carry semantic order and are never moved. Imports on either side are sorted independently
193
222
 
194
223
  ### Exports
195
224
 
package/README.zh.md CHANGED
@@ -185,6 +185,35 @@ import { useEffect, useState } from 'react';
185
185
 
186
186
  如果你希望保留原本分离的两条语句,把 `importOrderMergeDuplicates` 设为 `false` 即可。副作用导入(`import 'mod';`)因为顺序有语义,永远不会被合并。
187
187
 
188
+ #### 副作用导入
189
+
190
+ 副作用导入(`import 'mod'`)的顺序通常有运行时语义,例如 CSS 的层叠顺序、polyfill 必须在框架之前加载等。插件不会跨越副作用导入移动其他 import 语句:
191
+
192
+ 排序前:
193
+
194
+ <!-- prettier-ignore -->
195
+ ```typescript
196
+ import Button from './Button';
197
+ import App from './App';
198
+ import 'normalize.css';
199
+ import theme from './theme';
200
+ import Icon from './Icon';
201
+ ```
202
+
203
+ 排序后:
204
+
205
+ ```typescript
206
+ import App from './App';
207
+ import Button from './Button';
208
+
209
+ import 'normalize.css';
210
+
211
+ import Icon from './Icon';
212
+ import theme from './theme';
213
+ ```
214
+
215
+ 副作用导入两侧的 import 各自独立排序,副作用导入本身保持原位不动。
216
+
188
217
  排序规则:
189
218
 
190
219
  - import 按分组分类,分组内按字母序排列
@@ -192,7 +221,7 @@ import { useEffect, useState } from 'react';
192
221
  - 分组之间默认插入空行,可通过 `importOrderSeparation` 关闭
193
222
  - `type` import 默认拆成独立语句,可通过 `importOrderTypeImports` 调整为内联
194
223
  - 同一来源的多条 import 默认合并为一条,可通过 `importOrderMergeDuplicates` 关闭
195
- - 副作用导入(`import 'mod'`)顺序有语义,永远不参与合并或跨位移动
224
+ - 副作用导入(`import 'mod'`)其顺序有语义,不会被移动,两侧的 import 各自独立排序
196
225
 
197
226
  ### export
198
227
 
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
  }
@@ -121,6 +119,30 @@ var options = {
121
119
  }
122
120
  };
123
121
 
122
+ // src/utils.ts
123
+ function splitTopLevel(input, separator) {
124
+ const segments = [];
125
+ let current = "";
126
+ let depth = 0;
127
+ for (const char of input) {
128
+ if (char === "{" || char === "(" || char === "[") {
129
+ depth++;
130
+ } else if (char === "}" || char === ")" || char === "]") {
131
+ depth--;
132
+ }
133
+ if (char === separator && depth === 0) {
134
+ segments.push(current);
135
+ current = "";
136
+ continue;
137
+ }
138
+ current += char;
139
+ }
140
+ if (current.length > 0) {
141
+ segments.push(current);
142
+ }
143
+ return segments.map((segment) => segment.trim()).filter((segment) => segment.length > 0);
144
+ }
145
+
124
146
  // src/sort-exports.ts
125
147
  function sortExports(text, rawOptions) {
126
148
  const options2 = resolveSortOptions(rawOptions);
@@ -135,36 +157,14 @@ function sortExports(text, rawOptions) {
135
157
  const sorted = [...members].sort((a, b) => stripTypePrefix(a).localeCompare(stripTypePrefix(b), "en", {
136
158
  sensitivity: "base"
137
159
  }));
138
- const same = sorted.every((m, i) => m === members[i]);
139
- if (same) {
160
+ const unchanged = sorted.every((member, index) => member === members[index]);
161
+ if (unchanged) {
140
162
  return match;
141
163
  }
142
164
  const prefix = typeKeyword ? `export${typeKeyword}` : "export";
143
165
  return `${prefix} { ${sorted.join(", ")} }`;
144
166
  });
145
167
  }
146
- function splitTopLevel(input, separator) {
147
- const out = [];
148
- let buf = "";
149
- let depth = 0;
150
- for (const ch of input) {
151
- if (ch === "{" || ch === "(" || ch === "[") {
152
- depth++;
153
- } else if (ch === "}" || ch === ")" || ch === "]") {
154
- depth--;
155
- }
156
- if (ch === separator && depth === 0) {
157
- out.push(buf);
158
- buf = "";
159
- continue;
160
- }
161
- buf += ch;
162
- }
163
- if (buf.length > 0) {
164
- out.push(buf);
165
- }
166
- return out.map((s) => s.trim()).filter((s) => s.length > 0);
167
- }
168
168
  function stripTypePrefix(member) {
169
169
  return member.replace(/^type\s+/, "");
170
170
  }
@@ -195,62 +195,41 @@ function detectGroup(source) {
195
195
  }
196
196
  return "external";
197
197
  }
198
- function splitTopLevel2(input, separator) {
199
- const out = [];
200
- let buf = "";
201
- let depth = 0;
202
- for (const ch of input) {
203
- if (ch === "{" || ch === "(" || ch === "[") {
204
- depth++;
205
- } else if (ch === "}" || ch === ")" || ch === "]") {
206
- depth--;
207
- }
208
- if (ch === separator && depth === 0) {
209
- out.push(buf);
210
- buf = "";
211
- continue;
212
- }
213
- buf += ch;
214
- }
215
- if (buf.length > 0) {
216
- out.push(buf);
217
- }
218
- return out.map((s) => s.trim()).filter((s) => s.length > 0);
219
- }
198
+ var TYPE_PREFIX = /^type\s+(.+)$/s;
220
199
  function splitMembers(inner) {
221
- return splitTopLevel2(inner, ",").map((part) => {
222
- const isType = /^type\s+/.test(part);
223
- const name = isType ? part.replace(/^type\s+/, "").trim() : part;
224
- return { name, isType };
200
+ return splitTopLevel(inner, ",").map((part) => {
201
+ const match = TYPE_PREFIX.exec(part);
202
+ return match ? { name: match[1].trim(), isType: true } : { name: part, isType: false };
225
203
  });
226
204
  }
227
- function parseImport(stmt) {
228
- const trimmed = stmt.raw.trim();
229
- const leadingComments = stmt.leadingComments;
230
- const sideEffect = /^import\s*(['"])([^'"]+)\1\s*;?$/.exec(trimmed);
205
+ function parseImport(statement) {
206
+ const trimmed = statement.raw.trim();
207
+ const leadingComments = statement.leadingComments;
208
+ const sideEffect = /^import\s*(['"])([^'"]+)\1(?:\s+with\s*(\{[^}]*\}))?\s*;?$/.exec(trimmed);
231
209
  if (sideEffect) {
232
210
  return {
233
- raw: trimmed,
234
211
  source: sideEffect[2] ?? "",
235
212
  typeClause: false,
236
213
  sideEffect: true,
237
214
  defaultSpec: null,
238
215
  namespaceSpec: null,
239
216
  members: null,
217
+ attributes: sideEffect[3] ?? null,
240
218
  leadingComments
241
219
  };
242
220
  }
243
- const m = /^import\s+(type\s+)?([\s\S]+?)\s*from\s*(['"])([^'"]+)\3\s*;?$/.exec(trimmed);
244
- if (!m) {
221
+ const match = /^import\s+(type\s+)?([\s\S]+?)\s*from\s*(['"])([^'"]+)\3(?:\s+with\s*(\{[^}]*\}))?\s*;?$/.exec(trimmed);
222
+ if (!match) {
245
223
  return null;
246
224
  }
247
- const typeClause = Boolean(m[1]);
248
- const clause = (m[2] ?? "").trim();
249
- const source = m[4] ?? "";
225
+ const typeClause = Boolean(match[1]);
226
+ const clause = (match[2] ?? "").trim();
227
+ const source = match[4] ?? "";
228
+ const attributes = match[5] ?? null;
250
229
  let defaultSpec = null;
251
230
  let namespaceSpec = null;
252
231
  let members = null;
253
- for (const part of splitTopLevel2(clause, ",")) {
232
+ for (const part of splitTopLevel(clause, ",")) {
254
233
  if (part.startsWith("{")) {
255
234
  const inner = part.slice(1, part.lastIndexOf("}")).trim();
256
235
  members = inner ? splitMembers(inner) : [];
@@ -261,13 +240,13 @@ function parseImport(stmt) {
261
240
  }
262
241
  }
263
242
  return {
264
- raw: trimmed,
265
243
  source,
266
244
  typeClause,
267
245
  sideEffect: false,
268
246
  defaultSpec,
269
247
  namespaceSpec,
270
248
  members,
249
+ attributes,
271
250
  leadingComments
272
251
  };
273
252
  }
@@ -280,17 +259,21 @@ function extractImportBlock(text) {
280
259
  const start = first.index + (text[first.index] === `
281
260
  ` ? 1 : 0);
282
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;
283
264
  let cursor = start;
284
265
  while (cursor < text.length) {
285
- const chunkMatch = /^(?:[ \t]*(?:\/\/[^\n]*|\/\*[\s\S]*?\*\/)?[ \t]*\n)*/.exec(text.slice(cursor));
286
- const chunk = chunkMatch ? chunkMatch[0] : "";
287
- const afterSkip = cursor + chunk.length;
288
- const importMatch = /^[ \t]*(import\b[\s\S]*?(?:from\s*(['"])[^'"]+\2|(['"])[^'"]+\3)\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);
289
272
  if (!importMatch) {
290
273
  break;
291
274
  }
292
- const normalised = chunk.endsWith(`
293
- `) ? chunk.slice(0, -1) : chunk;
275
+ const normalised = skipped.endsWith(`
276
+ `) ? skipped.slice(0, -1) : skipped;
294
277
  const commentLines = normalised.length > 0 ? normalised.split(`
295
278
  `) : [];
296
279
  const leadingLines = [];
@@ -322,27 +305,23 @@ function extractImportBlock(text) {
322
305
  function renderMembers(members) {
323
306
  return members.map((member) => member.isType ? `type ${member.name}` : member.name).join(", ");
324
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
+ }
325
321
  function renderImport(importDecl) {
326
- const body = (() => {
327
- if (importDecl.sideEffect) {
328
- return `import '${importDecl.source}';`;
329
- }
330
- if (importDecl.typeClause) {
331
- const inner = importDecl.members ? `{ ${renderMembers(importDecl.members)} }` : "";
332
- return `import type ${inner} from '${importDecl.source}';`;
333
- }
334
- const leftParts = [];
335
- if (importDecl.defaultSpec) {
336
- leftParts.push(importDecl.defaultSpec);
337
- }
338
- if (importDecl.namespaceSpec) {
339
- leftParts.push(importDecl.namespaceSpec);
340
- }
341
- if (importDecl.members) {
342
- leftParts.push(`{ ${renderMembers(importDecl.members)} }`);
343
- }
344
- return `import ${leftParts.join(", ")} from '${importDecl.source}';`;
345
- })();
322
+ const suffix = importDecl.attributes ? ` with ${importDecl.attributes}` : "";
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};`;
346
325
  return importDecl.leadingComments + body;
347
326
  }
348
327
  function sortMembersAlpha(members) {
@@ -361,9 +340,9 @@ function normalizeTypeClause(importDecl) {
361
340
  function mergeImportsFromSameSource(imports) {
362
341
  const indexBySource = new Map;
363
342
  const result = [];
364
- for (const raw of imports) {
365
- const importDecl = normalizeTypeClause(raw);
366
- if (importDecl.sideEffect) {
343
+ for (const rawImport of imports) {
344
+ const importDecl = normalizeTypeClause(rawImport);
345
+ if (importDecl.sideEffect || importDecl.typeClause) {
367
346
  result.push(importDecl);
368
347
  continue;
369
348
  }
@@ -375,13 +354,13 @@ function mergeImportsFromSameSource(imports) {
375
354
  }
376
355
  const existing = result[existingIndex];
377
356
  result[existingIndex] = {
378
- raw: "",
379
357
  source: existing.source,
380
358
  typeClause: false,
381
359
  sideEffect: false,
382
360
  defaultSpec: existing.defaultSpec ?? importDecl.defaultSpec,
383
361
  namespaceSpec: existing.namespaceSpec ?? importDecl.namespaceSpec,
384
362
  members: existing.members === null && importDecl.members === null ? null : [...existing.members ?? [], ...importDecl.members ?? []],
363
+ attributes: existing.attributes ?? importDecl.attributes,
385
364
  leadingComments: existing.leadingComments
386
365
  };
387
366
  }
@@ -400,13 +379,13 @@ function applyTypeImports(importDecl, style) {
400
379
  const out = [];
401
380
  if (typeMembers2.length > 0) {
402
381
  out.push({
403
- raw: "",
404
382
  source: importDecl.source,
405
383
  typeClause: true,
406
384
  sideEffect: false,
407
385
  defaultSpec: null,
408
386
  namespaceSpec: null,
409
387
  members: sortMembersAlpha(typeMembers2.map((member) => ({ ...member, isType: false }))),
388
+ attributes: importDecl.attributes,
410
389
  leadingComments: importDecl.leadingComments
411
390
  });
412
391
  }
@@ -420,7 +399,7 @@ function applyTypeImports(importDecl, style) {
420
399
  }
421
400
  return out.length > 0 ? out : [importDecl];
422
401
  }
423
- const base = importDecl.typeClause ? {
402
+ const inlineBase = importDecl.typeClause ? {
424
403
  ...importDecl,
425
404
  typeClause: false,
426
405
  members: importDecl.members.map((member) => ({
@@ -429,38 +408,24 @@ function applyTypeImports(importDecl, style) {
429
408
  }))
430
409
  } : { ...importDecl, members: importDecl.members };
431
410
  if (style === "mixed") {
432
- return [{ ...base, members: sortMembersAlpha(base.members) }];
411
+ return [{ ...inlineBase, members: sortMembersAlpha(inlineBase.members) }];
433
412
  }
434
- const typeMembers = base.members.filter((m) => m.isType);
435
- const valueMembers = base.members.filter((m) => !m.isType);
413
+ const typeMembers = inlineBase.members.filter((member) => member.isType);
414
+ const valueMembers = inlineBase.members.filter((member) => !member.isType);
436
415
  const sortedTypes = sortMembersAlpha(typeMembers);
437
416
  const sortedValues = sortMembersAlpha(valueMembers);
438
417
  const ordered = style === "inline-first" ? [...sortedTypes, ...sortedValues] : [...sortedValues, ...sortedTypes];
439
- return [{ ...base, members: ordered }];
418
+ return [{ ...inlineBase, members: ordered }];
440
419
  }
441
- function sortImports(text, rawOptions) {
442
- const options2 = resolveSortOptions(rawOptions);
443
- if (!options2.importOrder) {
444
- return text;
445
- }
446
- const block = extractImportBlock(text);
447
- if (!block || block.statements.length === 0) {
448
- return text;
449
- }
450
- const parsed = block.statements.map((rawStmt) => parseImport(rawStmt)).filter((decl) => decl !== null);
451
- if (parsed.length === 0) {
452
- return text;
420
+ function sortSegment(imports, options2, groupIndex, fallback) {
421
+ if (imports.length === 0) {
422
+ return [];
453
423
  }
454
424
  const style = options2.importOrderTypeImports;
455
- const deduped = options2.importOrderMergeDuplicates ? mergeImportsFromSameSource(parsed) : parsed;
425
+ const deduped = options2.importOrderMergeDuplicates ? mergeImportsFromSameSource(imports) : imports;
456
426
  const rewritten = deduped.flatMap((importDecl) => applyTypeImports(importDecl, style));
457
- const groupIndex = new Map(options2.importOrderGroups.map((group, index) => [
458
- group,
459
- index
460
- ]));
461
- const fallback = options2.importOrderGroups.length;
462
427
  const decorated = rewritten.map((importDecl, index) => ({
463
- stmt: importDecl,
428
+ importDecl,
464
429
  group: detectGroup(importDecl.source),
465
430
  originalIndex: index
466
431
  }));
@@ -470,26 +435,75 @@ function sortImports(text, rawOptions) {
470
435
  if (groupOrderA !== groupOrderB) {
471
436
  return groupOrderA - groupOrderB;
472
437
  }
473
- const sourceA = a.stmt.source.toLowerCase();
474
- const sourceB = b.stmt.source.toLowerCase();
438
+ const sourceA = a.importDecl.source.toLowerCase();
439
+ const sourceB = b.importDecl.source.toLowerCase();
475
440
  if (sourceA !== sourceB) {
476
441
  return sourceA < sourceB ? -1 : 1;
477
442
  }
478
- if (a.stmt.typeClause !== b.stmt.typeClause) {
479
- return a.stmt.typeClause ? -1 : 1;
443
+ if (a.importDecl.typeClause !== b.importDecl.typeClause) {
444
+ return a.importDecl.typeClause ? -1 : 1;
480
445
  }
481
446
  return a.originalIndex - b.originalIndex;
482
447
  });
483
448
  const lines = [];
484
- let prevGroup = null;
449
+ let previousGroup = null;
485
450
  for (const item of decorated) {
486
- if (options2.importOrderSeparation && prevGroup !== null && item.group !== prevGroup) {
451
+ if (options2.importOrderSeparation && previousGroup !== null && item.group !== previousGroup) {
487
452
  lines.push("");
488
453
  }
489
- lines.push(renderImport(item.stmt));
490
- prevGroup = item.group;
454
+ lines.push(renderImport(item.importDecl));
455
+ previousGroup = item.group;
456
+ }
457
+ return lines;
458
+ }
459
+ function sortImports(text, rawOptions) {
460
+ const options2 = resolveSortOptions(rawOptions);
461
+ if (!options2.importOrder) {
462
+ return text;
463
+ }
464
+ const block = extractImportBlock(text);
465
+ if (!block || block.statements.length === 0) {
466
+ return text;
467
+ }
468
+ const parsed = block.statements.map((rawStatement) => parseImport(rawStatement)).filter((importDecl) => importDecl !== null);
469
+ if (parsed.length === 0) {
470
+ return text;
471
+ }
472
+ const groupIndex = new Map(options2.importOrderGroups.map((group, index) => [
473
+ group,
474
+ index
475
+ ]));
476
+ const fallback = options2.importOrderGroups.length;
477
+ const chunks = [];
478
+ let currentSegment = [];
479
+ for (const importDecl of parsed) {
480
+ if (importDecl.sideEffect) {
481
+ if (currentSegment.length > 0) {
482
+ chunks.push({ kind: "segment", imports: currentSegment });
483
+ currentSegment = [];
484
+ }
485
+ chunks.push({ kind: "side-effect", importDecl });
486
+ } else {
487
+ currentSegment.push(importDecl);
488
+ }
489
+ }
490
+ if (currentSegment.length > 0) {
491
+ chunks.push({ kind: "segment", imports: currentSegment });
492
+ }
493
+ const allLines = [];
494
+ let previousKind = null;
495
+ for (const chunk of chunks) {
496
+ if (previousKind !== null && previousKind !== chunk.kind && options2.importOrderSeparation) {
497
+ allLines.push("");
498
+ }
499
+ if (chunk.kind === "segment") {
500
+ allLines.push(...sortSegment(chunk.imports, options2, groupIndex, fallback));
501
+ } else {
502
+ allLines.push(renderImport(chunk.importDecl));
503
+ }
504
+ previousKind = chunk.kind;
491
505
  }
492
- const replacement = lines.join(`
506
+ const replacement = allLines.join(`
493
507
  `);
494
508
  const trailing = text.slice(block.end);
495
509
  const suffix = trailing.trim() ? `
@@ -644,15 +658,15 @@ function sortObjectKeysByOrder(record, order) {
644
658
  rest.push(entry);
645
659
  }
646
660
  }
647
- known.sort(([left], [right]) => (orderIndex.get(left) ?? 0) - (orderIndex.get(right) ?? 0));
648
- rest.sort(([left], [right]) => left.localeCompare(right, "en"));
661
+ known.sort(([a], [b]) => (orderIndex.get(a) ?? 0) - (orderIndex.get(b) ?? 0));
662
+ rest.sort(([a], [b]) => a.localeCompare(b, "en"));
649
663
  return Object.fromEntries([...known, ...rest]);
650
664
  }
651
665
  function sortObjectKeysAlpha(value) {
652
666
  if (!isPlainObject(value)) {
653
667
  return value;
654
668
  }
655
- return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right, "en")));
669
+ return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b, "en")));
656
670
  }
657
671
  function sortStringArrayAlpha(value) {
658
672
  return [...value].sort((a, b) => a.localeCompare(b, "en"));
@@ -682,7 +696,7 @@ function sortPackageJson(text, rawOptions) {
682
696
  if (!isPlainObject(parsed)) {
683
697
  return text;
684
698
  }
685
- let result = { ...parsed };
699
+ let result = parsed;
686
700
  for (const field of DEPENDENCY_FIELDS) {
687
701
  const dependencyMap = result[field];
688
702
  if (dependencyMap !== undefined && !exclude.has(field)) {
@@ -713,8 +727,8 @@ function wrap(parser, ...transforms) {
713
727
  ...parser,
714
728
  async preprocess(text, parserOptions) {
715
729
  let source = parser.preprocess ? await parser.preprocess(text, parserOptions) : text;
716
- for (const fn of transforms) {
717
- source = fn(source, parserOptions);
730
+ for (const transform of transforms) {
731
+ source = transform(source, parserOptions);
718
732
  }
719
733
  return source;
720
734
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prettier-plugin-sort",
3
- "version": "0.0.2",
3
+ "version": "0.1.0",
4
4
  "description": "An all-in-one Prettier plugin to sort imports, package.json keys, and more.",
5
5
  "keywords": [
6
6
  "imports",