prettier-plugin-sort 0.0.3 → 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.
Files changed (2) hide show
  1. package/dist/index.js +103 -111
  2. package/package.json +1 -1
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";
@@ -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,
@@ -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) {
@@ -345,9 +340,9 @@ function normalizeTypeClause(importDecl) {
345
340
  function mergeImportsFromSameSource(imports) {
346
341
  const indexBySource = 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
  }
@@ -359,7 +354,6 @@ function mergeImportsFromSameSource(imports) {
359
354
  }
360
355
  const existing = result[existingIndex];
361
356
  result[existingIndex] = {
362
- raw: "",
363
357
  source: existing.source,
364
358
  typeClause: false,
365
359
  sideEffect: false,
@@ -385,7 +379,6 @@ function applyTypeImports(importDecl, style) {
385
379
  const out = [];
386
380
  if (typeMembers2.length > 0) {
387
381
  out.push({
388
- raw: "",
389
382
  source: importDecl.source,
390
383
  typeClause: true,
391
384
  sideEffect: false,
@@ -406,7 +399,7 @@ function applyTypeImports(importDecl, style) {
406
399
  }
407
400
  return out.length > 0 ? out : [importDecl];
408
401
  }
409
- const base = importDecl.typeClause ? {
402
+ const inlineBase = importDecl.typeClause ? {
410
403
  ...importDecl,
411
404
  typeClause: false,
412
405
  members: importDecl.members.map((member) => ({
@@ -415,14 +408,14 @@ function applyTypeImports(importDecl, style) {
415
408
  }))
416
409
  } : { ...importDecl, members: importDecl.members };
417
410
  if (style === "mixed") {
418
- return [{ ...base, members: sortMembersAlpha(base.members) }];
411
+ return [{ ...inlineBase, members: sortMembersAlpha(inlineBase.members) }];
419
412
  }
420
- const typeMembers = base.members.filter((m) => m.isType);
421
- 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);
422
415
  const sortedTypes = sortMembersAlpha(typeMembers);
423
416
  const sortedValues = sortMembersAlpha(valueMembers);
424
417
  const ordered = style === "inline-first" ? [...sortedTypes, ...sortedValues] : [...sortedValues, ...sortedTypes];
425
- return [{ ...base, members: ordered }];
418
+ return [{ ...inlineBase, members: ordered }];
426
419
  }
427
420
  function sortSegment(imports, options2, groupIndex, fallback) {
428
421
  if (imports.length === 0) {
@@ -432,7 +425,7 @@ function sortSegment(imports, options2, groupIndex, fallback) {
432
425
  const deduped = options2.importOrderMergeDuplicates ? mergeImportsFromSameSource(imports) : imports;
433
426
  const rewritten = deduped.flatMap((importDecl) => applyTypeImports(importDecl, style));
434
427
  const decorated = rewritten.map((importDecl, index) => ({
435
- stmt: importDecl,
428
+ importDecl,
436
429
  group: detectGroup(importDecl.source),
437
430
  originalIndex: index
438
431
  }));
@@ -442,24 +435,24 @@ function sortSegment(imports, options2, groupIndex, fallback) {
442
435
  if (groupOrderA !== groupOrderB) {
443
436
  return groupOrderA - groupOrderB;
444
437
  }
445
- const sourceA = a.stmt.source.toLowerCase();
446
- const sourceB = b.stmt.source.toLowerCase();
438
+ const sourceA = a.importDecl.source.toLowerCase();
439
+ const sourceB = b.importDecl.source.toLowerCase();
447
440
  if (sourceA !== sourceB) {
448
441
  return sourceA < sourceB ? -1 : 1;
449
442
  }
450
- if (a.stmt.typeClause !== b.stmt.typeClause) {
451
- return a.stmt.typeClause ? -1 : 1;
443
+ if (a.importDecl.typeClause !== b.importDecl.typeClause) {
444
+ return a.importDecl.typeClause ? -1 : 1;
452
445
  }
453
446
  return a.originalIndex - b.originalIndex;
454
447
  });
455
448
  const lines = [];
456
- let prevGroup = null;
449
+ let previousGroup = null;
457
450
  for (const item of decorated) {
458
- if (options2.importOrderSeparation && prevGroup !== null && item.group !== prevGroup) {
451
+ if (options2.importOrderSeparation && previousGroup !== null && item.group !== previousGroup) {
459
452
  lines.push("");
460
453
  }
461
- lines.push(renderImport(item.stmt));
462
- prevGroup = item.group;
454
+ lines.push(renderImport(item.importDecl));
455
+ previousGroup = item.group;
463
456
  }
464
457
  return lines;
465
458
  }
@@ -472,7 +465,7 @@ function sortImports(text, rawOptions) {
472
465
  if (!block || block.statements.length === 0) {
473
466
  return text;
474
467
  }
475
- const parsed = block.statements.map((rawStmt) => parseImport(rawStmt)).filter((decl) => decl !== null);
468
+ const parsed = block.statements.map((rawStatement) => parseImport(rawStatement)).filter((importDecl) => importDecl !== null);
476
469
  if (parsed.length === 0) {
477
470
  return text;
478
471
  }
@@ -485,31 +478,30 @@ function sortImports(text, rawOptions) {
485
478
  let currentSegment = [];
486
479
  for (const importDecl of parsed) {
487
480
  if (importDecl.sideEffect) {
488
- chunks.push({ kind: "segment", imports: currentSegment });
489
- chunks.push({ kind: "side-effect", stmt: importDecl });
490
- currentSegment = [];
481
+ if (currentSegment.length > 0) {
482
+ chunks.push({ kind: "segment", imports: currentSegment });
483
+ currentSegment = [];
484
+ }
485
+ chunks.push({ kind: "side-effect", importDecl });
491
486
  } else {
492
487
  currentSegment.push(importDecl);
493
488
  }
494
489
  }
495
- chunks.push({ kind: "segment", imports: currentSegment });
490
+ if (currentSegment.length > 0) {
491
+ chunks.push({ kind: "segment", imports: currentSegment });
492
+ }
496
493
  const allLines = [];
494
+ let previousKind = null;
497
495
  for (const chunk of chunks) {
496
+ if (previousKind !== null && previousKind !== chunk.kind && options2.importOrderSeparation) {
497
+ allLines.push("");
498
+ }
498
499
  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);
500
+ allLines.push(...sortSegment(chunk.imports, options2, groupIndex, fallback));
507
501
  } else {
508
- if (allLines.length > 0 && options2.importOrderSeparation) {
509
- allLines.push("");
510
- }
511
- allLines.push(renderImport(chunk.stmt));
502
+ allLines.push(renderImport(chunk.importDecl));
512
503
  }
504
+ previousKind = chunk.kind;
513
505
  }
514
506
  const replacement = allLines.join(`
515
507
  `);
@@ -666,15 +658,15 @@ function sortObjectKeysByOrder(record, order) {
666
658
  rest.push(entry);
667
659
  }
668
660
  }
669
- known.sort(([left], [right]) => (orderIndex.get(left) ?? 0) - (orderIndex.get(right) ?? 0));
670
- 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"));
671
663
  return Object.fromEntries([...known, ...rest]);
672
664
  }
673
665
  function sortObjectKeysAlpha(value) {
674
666
  if (!isPlainObject(value)) {
675
667
  return value;
676
668
  }
677
- 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")));
678
670
  }
679
671
  function sortStringArrayAlpha(value) {
680
672
  return [...value].sort((a, b) => a.localeCompare(b, "en"));
@@ -704,7 +696,7 @@ function sortPackageJson(text, rawOptions) {
704
696
  if (!isPlainObject(parsed)) {
705
697
  return text;
706
698
  }
707
- let result = { ...parsed };
699
+ let result = parsed;
708
700
  for (const field of DEPENDENCY_FIELDS) {
709
701
  const dependencyMap = result[field];
710
702
  if (dependencyMap !== undefined && !exclude.has(field)) {
@@ -735,8 +727,8 @@ function wrap(parser, ...transforms) {
735
727
  ...parser,
736
728
  async preprocess(text, parserOptions) {
737
729
  let source = parser.preprocess ? await parser.preprocess(text, parserOptions) : text;
738
- for (const fn of transforms) {
739
- source = fn(source, parserOptions);
730
+ for (const transform of transforms) {
731
+ source = transform(source, parserOptions);
740
732
  }
741
733
  return source;
742
734
  }
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.0",
4
4
  "description": "An all-in-one Prettier plugin to sort imports, package.json keys, and more.",
5
5
  "keywords": [
6
6
  "imports",