prettier-plugin-sort 0.1.1 → 1.0.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/dist/index.js CHANGED
@@ -1,17 +1,19 @@
1
1
  // src/index.ts
2
- import { parsers as babelParsers } from "prettier/plugins/babel";
3
- import { parsers as typescriptParsers } from "prettier/plugins/typescript";
2
+ import acornPlugin from "prettier/plugins/acorn";
3
+ import babelPlugin from "prettier/plugins/babel";
4
+ import flowPlugin from "prettier/plugins/flow";
5
+ import meriyahPlugin from "prettier/plugins/meriyah";
6
+ import typescriptPlugin from "prettier/plugins/typescript";
4
7
 
5
8
  // src/options.ts
6
9
  var DEFAULT_SORT_OPTIONS = {
7
- importOrder: true,
8
- importOrderGroups: ["builtin", "external", "parent", "sibling", "index"],
9
- importOrderSeparation: true,
10
- importOrderTypeImports: "separate",
11
- importOrderMergeDuplicates: true,
12
- exportOrder: true,
13
- packageJsonOrder: true,
14
- packageJsonOrderExcludeKeys: []
10
+ esmImportSort: true,
11
+ esmImportGroups: ["builtin", "external", "parent", "sibling", "index"],
12
+ esmImportSeparation: true,
13
+ esmImportTypeStyle: "separate",
14
+ esmImportMerge: true,
15
+ esmExportSpecifierSort: true,
16
+ packageSort: true
15
17
  };
16
18
  var VALID_IMPORT_GROUPS = new Set([
17
19
  "builtin",
@@ -21,503 +23,250 @@ var VALID_IMPORT_GROUPS = new Set([
21
23
  "sibling",
22
24
  "index"
23
25
  ]);
24
- var VALID_TYPE_STYLES = new Set([
26
+ var VALID_TYPE_IMPORT_STYLES = new Set([
25
27
  "separate",
26
28
  "inline-first",
27
29
  "inline-last",
28
30
  "mixed"
29
31
  ]);
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
- }
36
- function resolveSortOptions(rawOptions) {
37
- const groups = Array.isArray(rawOptions.importOrderGroups) ? rawOptions.importOrderGroups.filter(isValidImportGroup) : [];
38
- const excludeKeys = Array.isArray(rawOptions.packageJsonOrderExcludeKeys) ? rawOptions.packageJsonOrderExcludeKeys.filter((key) => typeof key === "string") : [];
39
- const importOrderTypeImports = isValidTypeStyle(rawOptions.importOrderTypeImports) ? rawOptions.importOrderTypeImports : DEFAULT_SORT_OPTIONS.importOrderTypeImports;
32
+ var isValidImportGroup = (candidateValue) => typeof candidateValue === "string" && VALID_IMPORT_GROUPS.has(candidateValue);
33
+ var isValidTypeImportStyle = (candidateValue) => typeof candidateValue === "string" && VALID_TYPE_IMPORT_STYLES.has(candidateValue);
34
+ function resolveBooleanSortOption(prettierOptions, optionName) {
35
+ const configuredValue = prettierOptions[optionName];
36
+ if (typeof configuredValue === "boolean") {
37
+ return configuredValue;
38
+ }
39
+ return DEFAULT_SORT_OPTIONS[optionName];
40
+ }
41
+ function resolveSortOptions(prettierOptions) {
42
+ const configuredImportGroups = Array.isArray(prettierOptions.esmImportGroups) ? [...new Set(prettierOptions.esmImportGroups.filter(isValidImportGroup))] : [];
43
+ const remainingDefaultImportGroups = DEFAULT_SORT_OPTIONS.esmImportGroups.filter((importGroup) => !configuredImportGroups.includes(importGroup));
44
+ const resolvedImportGroups = configuredImportGroups.length > 0 ? [...configuredImportGroups, ...remainingDefaultImportGroups] : [...DEFAULT_SORT_OPTIONS.esmImportGroups];
45
+ const resolvedTypeImportStyle = isValidTypeImportStyle(prettierOptions.esmImportTypeStyle) ? prettierOptions.esmImportTypeStyle : DEFAULT_SORT_OPTIONS.esmImportTypeStyle;
40
46
  return {
41
- importOrder: resolveBoolean(rawOptions, "importOrder"),
42
- importOrderGroups: groups.length > 0 ? groups : [...DEFAULT_SORT_OPTIONS.importOrderGroups],
43
- importOrderSeparation: resolveBoolean(rawOptions, "importOrderSeparation"),
44
- importOrderTypeImports,
45
- importOrderMergeDuplicates: resolveBoolean(rawOptions, "importOrderMergeDuplicates"),
46
- exportOrder: resolveBoolean(rawOptions, "exportOrder"),
47
- packageJsonOrder: resolveBoolean(rawOptions, "packageJsonOrder"),
48
- packageJsonOrderExcludeKeys: excludeKeys
47
+ esmImportSort: resolveBooleanSortOption(prettierOptions, "esmImportSort"),
48
+ esmImportGroups: resolvedImportGroups,
49
+ esmImportSeparation: resolveBooleanSortOption(prettierOptions, "esmImportSeparation"),
50
+ esmImportTypeStyle: resolvedTypeImportStyle,
51
+ esmImportMerge: resolveBooleanSortOption(prettierOptions, "esmImportMerge"),
52
+ esmExportSpecifierSort: resolveBooleanSortOption(prettierOptions, "esmExportSpecifierSort"),
53
+ packageSort: resolveBooleanSortOption(prettierOptions, "packageSort")
49
54
  };
50
55
  }
51
56
  var options = {
52
- importOrder: {
57
+ esmImportSort: {
53
58
  type: "boolean",
54
- default: DEFAULT_SORT_OPTIONS.importOrder,
55
- category: "SortImports",
56
- description: "Sort `import` declarations in JS/TS files."
59
+ default: DEFAULT_SORT_OPTIONS.esmImportSort,
60
+ category: "ES Module Imports",
61
+ description: "Sort top-level ES module import declarations and named specifiers."
57
62
  },
58
- importOrderGroups: {
63
+ esmImportGroups: {
59
64
  type: "string",
60
65
  array: true,
61
- default: [{ value: [...DEFAULT_SORT_OPTIONS.importOrderGroups] }],
62
- category: "SortImports",
63
- description: 'Ordered list of import groups. Valid values: "builtin", "external", "internal", "parent", "sibling", "index". Each group is sorted alphabetically; unknown groups are ignored.'
66
+ default: [{ value: [...DEFAULT_SORT_OPTIONS.esmImportGroups] }],
67
+ category: "ES Module Imports",
68
+ description: 'Set import group order. Valid groups: "builtin", "external", "internal", "parent", "sibling", and "index". Unlisted groups sort after listed groups.'
64
69
  },
65
- importOrderSeparation: {
70
+ esmImportSeparation: {
66
71
  type: "boolean",
67
- default: DEFAULT_SORT_OPTIONS.importOrderSeparation,
68
- category: "SortImports",
69
- description: "Insert a blank line between adjacent import groups."
72
+ default: DEFAULT_SORT_OPTIONS.esmImportSeparation,
73
+ category: "ES Module Imports",
74
+ description: "Add blank lines between import groups and around side-effect import boundaries."
70
75
  },
71
- importOrderTypeImports: {
76
+ esmImportTypeStyle: {
72
77
  type: "choice",
73
- default: DEFAULT_SORT_OPTIONS.importOrderTypeImports,
74
- category: "SortImports",
75
- description: "How to place `type` imports relative to value imports.",
78
+ default: DEFAULT_SORT_OPTIONS.esmImportTypeStyle,
79
+ category: "ES Module Imports",
80
+ description: "Format named type imports as separate declarations or inline specifiers.",
76
81
  choices: [
77
82
  {
78
83
  value: "separate",
79
- description: "Keep `import type { … }` as its own statement."
84
+ description: "Use a separate import type declaration."
80
85
  },
81
86
  {
82
87
  value: "inline-first",
83
- description: "Inline inside braces, type specifiers before value specifiers."
88
+ description: "Place inline type specifiers before value specifiers."
84
89
  },
85
90
  {
86
91
  value: "inline-last",
87
- description: "Inline inside braces, type specifiers after value specifiers."
92
+ description: "Place inline type specifiers after value specifiers."
88
93
  },
89
94
  {
90
95
  value: "mixed",
91
- description: "Inline inside braces, alphabetical without distinguishing type from value."
96
+ description: "Use inline type specifiers and sort all specifiers together."
92
97
  }
93
98
  ]
94
99
  },
95
- importOrderMergeDuplicates: {
100
+ esmImportMerge: {
96
101
  type: "boolean",
97
- default: DEFAULT_SORT_OPTIONS.importOrderMergeDuplicates,
98
- category: "SortImports",
99
- description: "Merge multiple `import` statements from the same source into one. Side-effect imports are never merged."
102
+ default: DEFAULT_SORT_OPTIONS.esmImportMerge,
103
+ category: "ES Module Imports",
104
+ description: "Merge compatible import declarations from the same module."
100
105
  },
101
- exportOrder: {
106
+ esmExportSpecifierSort: {
102
107
  type: "boolean",
103
- default: DEFAULT_SORT_OPTIONS.exportOrder,
104
- category: "SortExports",
105
- description: "Sort named specifiers inside `export { }` alphabetically. Does not reorder export statements."
108
+ default: DEFAULT_SORT_OPTIONS.esmExportSpecifierSort,
109
+ category: "ES Module Exports",
110
+ description: "Sort named export specifiers without reordering export declarations."
106
111
  },
107
- packageJsonOrder: {
112
+ packageSort: {
108
113
  type: "boolean",
109
- default: DEFAULT_SORT_OPTIONS.packageJsonOrder,
110
- category: "SortPackageJson",
111
- description: "Sort top-level keys and string-array values inside `package.json`. Dependency maps are always alphabetised regardless of this option."
112
- },
113
- packageJsonOrderExcludeKeys: {
114
- type: "string",
115
- array: true,
116
- default: [{ value: [...DEFAULT_SORT_OPTIONS.packageJsonOrderExcludeKeys] }],
117
- category: "SortPackageJson",
118
- description: "Top-level `package.json` keys to leave untouched (no key reordering or array sorting). Takes priority over `packageJsonOrder`."
114
+ default: DEFAULT_SORT_OPTIONS.packageSort,
115
+ category: "package.json",
116
+ description: "Sort package.json fields using the sort-package-json community convention."
119
117
  }
120
118
  };
121
119
 
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
-
146
- // src/sort-exports.ts
147
- function sortExports(text, rawOptions) {
148
- const options2 = resolveSortOptions(rawOptions);
149
- if (!options2.exportOrder) {
150
- return text;
151
- }
152
- return text.replace(/export(\s+type)?\s*\{([^}]*)\}/g, (match, typeKeyword, inner) => {
153
- const members = splitTopLevel(inner, ",");
154
- if (members.length <= 1) {
155
- return match;
156
- }
157
- const sorted = [...members].sort((a, b) => stripTypePrefix(a).localeCompare(stripTypePrefix(b), "en", {
158
- sensitivity: "base"
159
- }));
160
- const unchanged = sorted.every((member, index) => member === members[index]);
161
- if (unchanged) {
162
- return match;
163
- }
164
- const prefix = typeKeyword ? `export${typeKeyword}` : "export";
165
- return `${prefix} { ${sorted.join(", ")} }`;
166
- });
167
- }
168
- function stripTypePrefix(member) {
169
- return member.replace(/^type\s+/, "");
170
- }
120
+ // src/sort-package.ts
121
+ import findMinimumSemanticVersion from "semver/ranges/min-version.js";
171
122
 
172
- // src/sort-imports.ts
173
- import { builtinModules } from "node:module";
174
- var NODE_BUILTINS = new Set(builtinModules);
175
- function detectGroup(source) {
176
- if (source === "bun" || source.startsWith("bun:") || source.startsWith("node:")) {
177
- return "builtin";
178
- }
179
- const slashIndex = source.indexOf("/");
180
- const head = slashIndex === -1 ? source : source.slice(0, slashIndex);
181
- if (head && NODE_BUILTINS.has(head)) {
182
- return "builtin";
183
- }
184
- if (source === "." || source === "./" || /^\.\/index(\.[a-z]+)?$/.test(source)) {
185
- return "index";
186
- }
187
- if (source.startsWith("../") || source === "..") {
188
- return "parent";
189
- }
190
- if (source.startsWith("./")) {
191
- return "sibling";
123
+ // src/parser-ast.ts
124
+ function getAstNodeTextRange(node) {
125
+ if (!node) {
126
+ return null;
192
127
  }
193
- if (source.startsWith("/") || source.startsWith("~") || source.startsWith("@/")) {
194
- return "internal";
128
+ const [start = NaN, end = NaN] = node.range ?? [node.start, node.end];
129
+ const isOffsetRangeValid = [start, end].every(Number.isSafeInteger) && start >= 0 && end >= start;
130
+ if (!isOffsetRangeValid) {
131
+ return null;
195
132
  }
196
- return "external";
133
+ return { start, end };
197
134
  }
198
- var TYPE_PREFIX = /^type\s+(.+)$/s;
199
- function splitMembers(inner) {
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 };
203
- });
135
+ function getSortedAstCommentsWithTextRanges(comments) {
136
+ return comments.flatMap((comment) => {
137
+ const textRange = getAstNodeTextRange(comment);
138
+ return textRange ? [{ comment, textRange }] : [];
139
+ }).sort((left, right) => left.textRange.start - right.textRange.start || left.textRange.end - right.textRange.end);
204
140
  }
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);
209
- if (sideEffect) {
210
- return {
211
- source: sideEffect[2] ?? "",
212
- typeClause: false,
213
- sideEffect: true,
214
- defaultSpec: null,
215
- namespaceSpec: null,
216
- members: null,
217
- attributes: sideEffect[3] ?? null,
218
- leadingComments
219
- };
220
- }
221
- const match = /^import\s+(type\s+)?([\s\S]+?)\s*from\s*(['"])([^'"]+)\3(?:\s+with\s*(\{[^}]*\}))?\s*;?$/.exec(trimmed);
222
- if (!match) {
223
- return null;
224
- }
225
- const typeClause = Boolean(match[1]);
226
- const clause = (match[2] ?? "").trim();
227
- const source = match[4] ?? "";
228
- const attributes = match[5] ?? null;
229
- let defaultSpec = null;
230
- let namespaceSpec = null;
231
- let members = null;
232
- for (const part of splitTopLevel(clause, ",")) {
233
- if (part.startsWith("{")) {
234
- const inner = part.slice(1, part.lastIndexOf("}")).trim();
235
- members = inner ? splitMembers(inner) : [];
236
- } else if (part.startsWith("*")) {
237
- namespaceSpec = part;
141
+ function findCommentIndexAtOrAfter(sortedComments, sourceIndex) {
142
+ let searchStartIndex = 0;
143
+ let searchEndIndex = sortedComments.length;
144
+ while (searchStartIndex < searchEndIndex) {
145
+ const middleIndex = Math.floor((searchStartIndex + searchEndIndex) / 2);
146
+ const middleCommentStart = sortedComments[middleIndex].textRange.start;
147
+ if (middleCommentStart < sourceIndex) {
148
+ searchStartIndex = middleIndex + 1;
238
149
  } else {
239
- defaultSpec = part;
150
+ searchEndIndex = middleIndex;
240
151
  }
241
152
  }
242
- return {
243
- source,
244
- typeClause,
245
- sideEffect: false,
246
- defaultSpec,
247
- namespaceSpec,
248
- members,
249
- attributes,
250
- leadingComments
251
- };
153
+ return searchStartIndex;
252
154
  }
253
- function extractImportBlock(text) {
254
- const firstRe = /(?:^|\n)(?:[ \t]*(?:\/\/[^\n]*|\/\*[\s\S]*?\*\/)[ \t]*\n)*[ \t]*import\b(?![.(])/;
255
- const first = firstRe.exec(text);
256
- if (!first) {
155
+ function getAstNodeName(node) {
156
+ if (!node) {
257
157
  return null;
258
158
  }
259
- const start = first.index + (text[first.index] === `
260
- ` ? 1 : 0);
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;
264
- let cursor = start;
265
- while (cursor < text.length) {
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
- if (!importMatch) {
273
- break;
274
- }
275
- const normalised = skipped.endsWith(`
276
- `) ? skipped.slice(0, -1) : skipped;
277
- const commentLines = normalised.length > 0 ? normalised.split(`
278
- `) : [];
279
- const leadingLines = [];
280
- for (let i = commentLines.length - 1;i >= 0; i--) {
281
- const line = commentLines[i];
282
- if (line === undefined) {
283
- continue;
284
- }
285
- if (line.trim() === "") {
286
- break;
287
- }
288
- leadingLines.unshift(line);
289
- }
290
- const leadingComments = leadingLines.length > 0 ? leadingLines.join(`
291
- `) + `
292
- ` : "";
293
- const statement = importMatch[1];
294
- if (!statement) {
295
- break;
296
- }
297
- statements.push({ raw: statement.trim(), leadingComments });
298
- cursor = afterSkip + importMatch[0].length;
159
+ if (typeof node.name === "string") {
160
+ return node.name;
299
161
  }
300
- if (statements.length === 0) {
301
- return null;
162
+ switch (typeof node.value) {
163
+ case "string":
164
+ case "number":
165
+ case "boolean":
166
+ return String(node.value);
167
+ default:
168
+ return null;
302
169
  }
303
- return { start, end: cursor, statements };
304
170
  }
305
- function renderMembers(members) {
306
- return members.map((member) => member.isType ? `type ${member.name}` : member.name).join(", ");
171
+ function getAstCommentText(comment) {
172
+ return typeof comment.value === "string" ? comment.value : null;
307
173
  }
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);
174
+ function getProgramStatements(parserAst) {
175
+ if (Array.isArray(parserAst.body)) {
176
+ return parserAst.body;
315
177
  }
316
- if (importDecl.members) {
317
- parts.push(`{ ${renderMembers(importDecl.members)} }`);
178
+ const programNode = parserAst.program;
179
+ if (!programNode || !Array.isArray(programNode.body)) {
180
+ return [];
318
181
  }
319
- return parts.join(", ");
320
- }
321
- function renderImport(importDecl) {
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};`;
325
- return importDecl.leadingComments + body;
182
+ return programNode.body;
326
183
  }
327
- function sortMembersAlpha(members) {
328
- return [...members].sort((a, b) => a.name.localeCompare(b.name, "en", { sensitivity: "base" }));
329
- }
330
- function normalizeTypeClause(importDecl) {
331
- if (!importDecl.typeClause || importDecl.members === null) {
332
- return importDecl;
184
+ function getProgramComments(parserAst) {
185
+ if (Array.isArray(parserAst.comments)) {
186
+ return parserAst.comments;
333
187
  }
334
- return {
335
- ...importDecl,
336
- typeClause: false,
337
- members: importDecl.members.map((member) => ({ ...member, isType: true }))
338
- };
188
+ const programNode = parserAst.program;
189
+ if (!programNode || !Array.isArray(programNode.comments)) {
190
+ return [];
191
+ }
192
+ return programNode.comments;
339
193
  }
340
- function mergeImportsFromSameSource(imports) {
341
- const indexByKey = new Map;
342
- const result = [];
343
- for (const rawImport of imports) {
344
- const importDecl = normalizeTypeClause(rawImport);
345
- if (importDecl.sideEffect || importDecl.typeClause) {
346
- result.push(importDecl);
347
- continue;
194
+ function isSourceRangeWhitespaceOrComments(sourceText, sourceRange, sortedComments) {
195
+ let sourceIndex = sourceRange.start;
196
+ let commentIndex = Math.max(findCommentIndexAtOrAfter(sortedComments, sourceRange.start) - 1, 0);
197
+ for (;commentIndex < sortedComments.length; commentIndex++) {
198
+ const parserComment = sortedComments[commentIndex];
199
+ if (!parserComment || parserComment.textRange.start >= sourceRange.end) {
200
+ break;
348
201
  }
349
- const mergeKey = `${importDecl.source}\x00${importDecl.attributes ?? ""}`;
350
- const existingIndex = indexByKey.get(mergeKey);
351
- if (existingIndex === undefined) {
352
- indexByKey.set(mergeKey, result.length);
353
- result.push(importDecl);
202
+ if (parserComment.textRange.end <= sourceIndex) {
354
203
  continue;
355
204
  }
356
- const existing = result[existingIndex];
357
- result[existingIndex] = {
358
- source: existing.source,
359
- typeClause: false,
360
- sideEffect: false,
361
- defaultSpec: existing.defaultSpec ?? importDecl.defaultSpec,
362
- namespaceSpec: existing.namespaceSpec ?? importDecl.namespaceSpec,
363
- members: existing.members === null && importDecl.members === null ? null : [...existing.members ?? [], ...importDecl.members ?? []],
364
- attributes: existing.attributes,
365
- leadingComments: existing.leadingComments
366
- };
367
- }
368
- return result;
369
- }
370
- function applyTypeImports(importDecl, style) {
371
- if (importDecl.sideEffect || !importDecl.members) {
372
- return [importDecl];
373
- }
374
- if (style === "separate") {
375
- if (importDecl.typeClause) {
376
- return [importDecl];
377
- }
378
- const typeMembers2 = importDecl.members.filter((member) => member.isType);
379
- const valueMembers2 = importDecl.members.filter((member) => !member.isType);
380
- const out = [];
381
- if (typeMembers2.length > 0) {
382
- out.push({
383
- source: importDecl.source,
384
- typeClause: true,
385
- sideEffect: false,
386
- defaultSpec: null,
387
- namespaceSpec: null,
388
- members: sortMembersAlpha(typeMembers2.map((member) => ({ ...member, isType: false }))),
389
- attributes: importDecl.attributes,
390
- leadingComments: importDecl.leadingComments
391
- });
392
- }
393
- const hasValueBody = valueMembers2.length > 0 || importDecl.defaultSpec !== null || importDecl.namespaceSpec !== null;
394
- if (hasValueBody) {
395
- out.push({
396
- ...importDecl,
397
- members: valueMembers2.length > 0 ? sortMembersAlpha(valueMembers2) : null,
398
- leadingComments: typeMembers2.length > 0 ? "" : importDecl.leadingComments
399
- });
205
+ const commentStart = Math.max(parserComment.textRange.start, sourceRange.start);
206
+ if (sourceText.slice(sourceIndex, commentStart).trim() !== "") {
207
+ return false;
400
208
  }
401
- return out.length > 0 ? out : [importDecl];
402
- }
403
- const inlineBase = importDecl.typeClause ? {
404
- ...importDecl,
405
- typeClause: false,
406
- members: importDecl.members.map((member) => ({
407
- ...member,
408
- isType: true
409
- }))
410
- } : { ...importDecl, members: importDecl.members };
411
- if (style === "mixed") {
412
- return [{ ...inlineBase, members: sortMembersAlpha(inlineBase.members) }];
413
- }
414
- const typeMembers = inlineBase.members.filter((member) => member.isType);
415
- const valueMembers = inlineBase.members.filter((member) => !member.isType);
416
- const sortedTypes = sortMembersAlpha(typeMembers);
417
- const sortedValues = sortMembersAlpha(valueMembers);
418
- const ordered = style === "inline-first" ? [...sortedTypes, ...sortedValues] : [...sortedValues, ...sortedTypes];
419
- return [{ ...inlineBase, members: ordered }];
420
- }
421
- function sortSegment(imports, options2, groupIndex, fallback) {
422
- if (imports.length === 0) {
423
- return [];
209
+ sourceIndex = Math.min(parserComment.textRange.end, sourceRange.end);
424
210
  }
425
- const style = options2.importOrderTypeImports;
426
- const deduped = options2.importOrderMergeDuplicates ? mergeImportsFromSameSource(imports) : imports;
427
- const rewritten = deduped.flatMap((importDecl) => applyTypeImports(importDecl, style));
428
- const decorated = rewritten.map((importDecl, index) => ({
429
- importDecl,
430
- group: detectGroup(importDecl.source),
431
- originalIndex: index
432
- }));
433
- decorated.sort((a, b) => {
434
- const groupOrderA = groupIndex.get(a.group) ?? fallback;
435
- const groupOrderB = groupIndex.get(b.group) ?? fallback;
436
- if (groupOrderA !== groupOrderB) {
437
- return groupOrderA - groupOrderB;
211
+ return sourceText.slice(sourceIndex, sourceRange.end).trim() === "";
212
+ }
213
+ function isPrettierIgnored(sourceText, statementRange, sortedComments) {
214
+ let leadingCommentsStart = statementRange.start;
215
+ for (let commentIndex = findCommentIndexAtOrAfter(sortedComments, statementRange.start) - 1;commentIndex >= 0; commentIndex--) {
216
+ const parserComment = sortedComments[commentIndex];
217
+ if (!parserComment || parserComment.textRange.end > statementRange.start) {
218
+ continue;
438
219
  }
439
- const sourceA = a.importDecl.source.toLowerCase();
440
- const sourceB = b.importDecl.source.toLowerCase();
441
- if (sourceA !== sourceB) {
442
- return sourceA < sourceB ? -1 : 1;
220
+ const { comment, textRange } = parserComment;
221
+ const followingText = sourceText.slice(textRange.end, leadingCommentsStart);
222
+ if (followingText.trim() !== "") {
223
+ break;
443
224
  }
444
- if (a.importDecl.typeClause !== b.importDecl.typeClause) {
445
- return a.importDecl.typeClause ? -1 : 1;
225
+ if (followingText.includes(`
226
+ `)) {
227
+ const commentLineStart = sourceText.lastIndexOf(`
228
+ `, textRange.start - 1) + 1;
229
+ const isCommentLinePrefixEmpty = isSourceRangeWhitespaceOrComments(sourceText, { start: commentLineStart, end: textRange.start }, sortedComments);
230
+ if (!isCommentLinePrefixEmpty) {
231
+ break;
232
+ }
446
233
  }
447
- return a.originalIndex - b.originalIndex;
448
- });
449
- const lines = [];
450
- let previousGroup = null;
451
- for (const item of decorated) {
452
- if (options2.importOrderSeparation && previousGroup !== null && item.group !== previousGroup) {
453
- lines.push("");
234
+ const commentText = getAstCommentText(comment);
235
+ const isPrettierIgnoreComment = commentText?.trim() === "prettier-ignore";
236
+ if (isPrettierIgnoreComment) {
237
+ return true;
454
238
  }
455
- lines.push(renderImport(item.importDecl));
456
- previousGroup = item.group;
457
- }
458
- return lines;
459
- }
460
- function sortImports(text, rawOptions) {
461
- const options2 = resolveSortOptions(rawOptions);
462
- if (!options2.importOrder) {
463
- return text;
464
- }
465
- const block = extractImportBlock(text);
466
- if (!block || block.statements.length === 0) {
467
- return text;
468
- }
469
- const parsed = block.statements.map((rawStatement) => parseImport(rawStatement)).filter((importDecl) => importDecl !== null);
470
- if (parsed.length === 0) {
471
- return text;
239
+ leadingCommentsStart = textRange.start;
472
240
  }
473
- const groupIndex = new Map(options2.importOrderGroups.map((group, index) => [
474
- group,
475
- index
476
- ]));
477
- const fallback = options2.importOrderGroups.length;
478
- const chunks = [];
479
- let currentSegment = [];
480
- for (const importDecl of parsed) {
481
- if (importDecl.sideEffect) {
482
- if (currentSegment.length > 0) {
483
- chunks.push({ kind: "segment", imports: currentSegment });
484
- currentSegment = [];
485
- }
486
- chunks.push({ kind: "side-effect", importDecl });
487
- } else {
488
- currentSegment.push(importDecl);
241
+ let trailingCommentsEnd = statementRange.end;
242
+ for (let commentIndex = findCommentIndexAtOrAfter(sortedComments, statementRange.end);commentIndex < sortedComments.length; commentIndex++) {
243
+ const parserComment = sortedComments[commentIndex];
244
+ if (!parserComment) {
245
+ break;
489
246
  }
490
- }
491
- if (currentSegment.length > 0) {
492
- chunks.push({ kind: "segment", imports: currentSegment });
493
- }
494
- const allLines = [];
495
- let previousKind = null;
496
- for (const chunk of chunks) {
497
- if (previousKind !== null && previousKind !== chunk.kind && options2.importOrderSeparation) {
498
- allLines.push("");
247
+ const { comment, textRange } = parserComment;
248
+ const textBeforeComment = sourceText.slice(trailingCommentsEnd, textRange.start);
249
+ if (textBeforeComment.includes(`
250
+ `) || textBeforeComment.trim() !== "") {
251
+ break;
499
252
  }
500
- if (chunk.kind === "segment") {
501
- allLines.push(...sortSegment(chunk.imports, options2, groupIndex, fallback));
502
- } else {
503
- allLines.push(renderImport(chunk.importDecl));
253
+ const commentLineEnd = sourceText.indexOf(`
254
+ `, textRange.end);
255
+ const isCommentLastOnLine = isSourceRangeWhitespaceOrComments(sourceText, {
256
+ start: textRange.end,
257
+ end: commentLineEnd < 0 ? sourceText.length : commentLineEnd
258
+ }, sortedComments);
259
+ const commentText = getAstCommentText(comment);
260
+ if (isCommentLastOnLine && commentText?.trim() === "prettier-ignore") {
261
+ return true;
504
262
  }
505
- previousKind = chunk.kind;
263
+ trailingCommentsEnd = textRange.end;
506
264
  }
507
- const replacement = allLines.join(`
508
- `);
509
- const trailing = text.slice(block.end);
510
- const suffix = trailing.trim() ? `
511
-
512
- ` + trailing.trimStart() : trailing;
513
- return text.slice(0, block.start) + replacement + suffix;
265
+ return false;
514
266
  }
515
267
 
516
- // src/sort-package.ts
517
- import path from "node:path";
518
-
519
- // src/order-package.ts
520
- var PACKAGE_JSON_TOP_LEVEL_ORDER = [
268
+ // src/utils/package-rules.ts
269
+ var PACKAGE_JSON_FIELD_ORDER = [
521
270
  "$schema",
522
271
  "name",
523
272
  "displayName",
@@ -566,6 +315,7 @@ var PACKAGE_JSON_TOP_LEVEL_ORDER = [
566
315
  "binary",
567
316
  "scripts",
568
317
  "betterScripts",
318
+ "wireit",
569
319
  "l10n",
570
320
  "contributes",
571
321
  "activationEvents",
@@ -629,122 +379,1618 @@ var PACKAGE_JSON_TOP_LEVEL_ORDER = [
629
379
  "markdown",
630
380
  "pnpm"
631
381
  ];
632
- var DEPENDENCY_FIELDS = [
382
+ var DIRECTORY_FIELD_ORDER = [
383
+ "lib",
384
+ "bin",
385
+ "man",
386
+ "doc",
387
+ "example",
388
+ "test"
389
+ ];
390
+ var ESLINT_CONFIG_FIELD_ORDER = [
391
+ "files",
392
+ "excludedFiles",
393
+ "env",
394
+ "parser",
395
+ "parserOptions",
396
+ "settings",
397
+ "plugins",
398
+ "extends",
399
+ "rules",
400
+ "overrides",
401
+ "globals",
402
+ "processor",
403
+ "noInlineConfig",
404
+ "reportUnusedDisableDirectives"
405
+ ];
406
+ var GIT_HOOK_ORDER = [
407
+ "applypatch-msg",
408
+ "pre-applypatch",
409
+ "post-applypatch",
410
+ "pre-commit",
411
+ "pre-merge-commit",
412
+ "prepare-commit-msg",
413
+ "commit-msg",
414
+ "post-commit",
415
+ "pre-rebase",
416
+ "post-checkout",
417
+ "post-merge",
418
+ "pre-push",
419
+ "pre-receive",
420
+ "update",
421
+ "proc-receive",
422
+ "post-receive",
423
+ "post-update",
424
+ "reference-transaction",
425
+ "push-to-checkout",
426
+ "pre-auto-gc",
427
+ "post-rewrite",
428
+ "sendemail-validate",
429
+ "fsmonitor-watchman",
430
+ "p4-changelist",
431
+ "p4-prepare-changelist",
432
+ "p4-post-changelist",
433
+ "p4-pre-submit",
434
+ "post-index-change"
435
+ ];
436
+ var NPM_LIFECYCLE_SCRIPT_NAMES = new Set([
437
+ "install",
438
+ "pack",
439
+ "prepare",
440
+ "publish",
441
+ "restart",
442
+ "shrinkwrap",
443
+ "start",
444
+ "stop",
445
+ "test",
446
+ "uninstall",
447
+ "version"
448
+ ]);
449
+ var PNPM_CONFIG_FIELD_ORDER = [
450
+ "peerDependencyRules",
451
+ "neverBuiltDependencies",
452
+ "onlyBuiltDependencies",
453
+ "onlyBuiltDependenciesFile",
454
+ "allowedDeprecatedVersions",
455
+ "allowNonAppliedPatches",
456
+ "updateConfig",
457
+ "auditConfig",
458
+ "requiredScripts",
459
+ "supportedArchitectures",
460
+ "overrides",
461
+ "patchedDependencies",
462
+ "packageExtensions"
463
+ ];
464
+ var WIREIT_SCRIPT_FIELD_ORDER = [
465
+ "command",
633
466
  "dependencies",
634
- "devDependencies",
635
- "peerDependencies",
636
- "optionalDependencies",
637
- "bundledDependencies",
638
- "bundleDependencies",
639
- "resolutions",
640
- "overrides"
467
+ "files",
468
+ "output"
641
469
  ];
642
470
 
643
471
  // src/sort-package.ts
644
- function isPlainObject(value) {
645
- return typeof value === "object" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
472
+ class JsonSourceLiteral {
473
+ value;
474
+ sourceText;
475
+ constructor(value, sourceText) {
476
+ this.value = value;
477
+ this.sourceText = sourceText;
478
+ }
479
+ }
480
+ var SEQUENTIAL_SCRIPT_PATTERN = /(?<=^|[\s&;<>|(])(?:run-s|npm-run-all2? .*(?:--sequential|--serial|-s))(?=$|[\s&;<>|)])/;
481
+ function compareText(leftText, rightText) {
482
+ if (leftText === rightText) {
483
+ return 0;
484
+ }
485
+ return leftText < rightText ? -1 : 1;
486
+ }
487
+ function isJsonObject(value) {
488
+ return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof JsonSourceLiteral);
489
+ }
490
+ function getJsonPathKey(jsonPath) {
491
+ return JSON.stringify(jsonPath);
492
+ }
493
+ function getJsonString(jsonValue) {
494
+ if (typeof jsonValue === "string") {
495
+ return jsonValue;
496
+ }
497
+ if (jsonValue instanceof JsonSourceLiteral && typeof jsonValue.value === "string") {
498
+ return jsonValue.value;
499
+ }
500
+ return null;
501
+ }
502
+ function isJsonStringValue(jsonValue) {
503
+ return getJsonString(jsonValue) !== null;
504
+ }
505
+ function isParserJsonAstNode(value) {
506
+ return typeof value === "object" && value !== null && "type" in value && typeof value.type === "string";
507
+ }
508
+ function preserveJsonSourceLiterals(jsonValue, parserJsonNode, sourceText, fieldNameSourceTexts, jsonPath = []) {
509
+ if (parserJsonNode.type === "JsonRoot") {
510
+ const rootNode = parserJsonNode.node;
511
+ if (!isParserJsonAstNode(rootNode)) {
512
+ return;
513
+ }
514
+ return preserveJsonSourceLiterals(jsonValue, rootNode, sourceText, fieldNameSourceTexts, jsonPath);
515
+ }
516
+ const isStringLiteral = parserJsonNode.type === "StringLiteral";
517
+ const isNumberLiteral = parserJsonNode.type === "NumericLiteral";
518
+ const isNegativeNumberLiteral = parserJsonNode.type === "UnaryExpression" && parserJsonNode.operator === "-" && parserJsonNode.argument?.type === "NumericLiteral";
519
+ if (isStringLiteral || isNumberLiteral || isNegativeNumberLiteral) {
520
+ const literalRange = getAstNodeTextRange(parserJsonNode);
521
+ const isLiteralValueValid = isStringLiteral && typeof jsonValue === "string" || !isStringLiteral && typeof jsonValue === "number";
522
+ if (!literalRange || !isLiteralValueValid) {
523
+ return;
524
+ }
525
+ return new JsonSourceLiteral(jsonValue, sourceText.slice(literalRange.start, literalRange.end));
526
+ }
527
+ if (parserJsonNode.type === "ObjectExpression") {
528
+ if (!isJsonObject(jsonValue) || !Array.isArray(parserJsonNode.properties)) {
529
+ return;
530
+ }
531
+ const jsonObject = { ...jsonValue };
532
+ const propertyNames = new Set;
533
+ for (const propertyNode of parserJsonNode.properties) {
534
+ const propertyName = getAstNodeName(propertyNode.key);
535
+ const propertyNameRange = getAstNodeTextRange(propertyNode.key);
536
+ const propertyValueNode = propertyNode.value;
537
+ if (propertyNode.type !== "ObjectProperty" || propertyName === null || !propertyNameRange || propertyNames.has(propertyName) || !Object.hasOwn(jsonValue, propertyName) || !isParserJsonAstNode(propertyValueNode)) {
538
+ return;
539
+ }
540
+ propertyNames.add(propertyName);
541
+ const propertyPath = [...jsonPath, propertyName];
542
+ const propertyValue = preserveJsonSourceLiterals(jsonValue[propertyName], propertyValueNode, sourceText, fieldNameSourceTexts, propertyPath);
543
+ if (propertyValue === undefined) {
544
+ return;
545
+ }
546
+ fieldNameSourceTexts.set(getJsonPathKey(propertyPath), sourceText.slice(propertyNameRange.start, propertyNameRange.end));
547
+ jsonObject[propertyName] = propertyValue;
548
+ }
549
+ return jsonObject;
550
+ }
551
+ if (parserJsonNode.type === "ArrayExpression") {
552
+ if (!Array.isArray(jsonValue) || !Array.isArray(parserJsonNode.elements) || jsonValue.length !== parserJsonNode.elements.length) {
553
+ return;
554
+ }
555
+ const jsonArray = [];
556
+ for (const [
557
+ elementIndex,
558
+ elementNode
559
+ ] of parserJsonNode.elements.entries()) {
560
+ if (!elementNode) {
561
+ return;
562
+ }
563
+ const elementValue = preserveJsonSourceLiterals(jsonValue[elementIndex], elementNode, sourceText, fieldNameSourceTexts, [...jsonPath, elementIndex]);
564
+ if (elementValue === undefined) {
565
+ return;
566
+ }
567
+ jsonArray.push(elementValue);
568
+ }
569
+ return jsonArray;
570
+ }
571
+ return jsonValue;
572
+ }
573
+ function serializeJsonValue(jsonValue, fieldNameSourceTexts, jsonPath = []) {
574
+ if (jsonValue instanceof JsonSourceLiteral) {
575
+ return jsonValue.sourceText;
576
+ }
577
+ if (typeof jsonValue === "number" || typeof jsonValue === "string") {
578
+ return null;
579
+ }
580
+ if (Array.isArray(jsonValue)) {
581
+ const serializedValues = [];
582
+ for (const [arrayIndex, arrayValue] of jsonValue.entries()) {
583
+ const serializedValue = serializeJsonValue(arrayValue, fieldNameSourceTexts, [...jsonPath, arrayIndex]);
584
+ if (serializedValue === null) {
585
+ return null;
586
+ }
587
+ serializedValues.push(serializedValue);
588
+ }
589
+ return `[${serializedValues.join(",")}]`;
590
+ }
591
+ if (isJsonObject(jsonValue)) {
592
+ const serializedFields = [];
593
+ for (const [fieldName, fieldValue] of Object.entries(jsonValue)) {
594
+ const fieldPath = [...jsonPath, fieldName];
595
+ const fieldNameSourceText = fieldNameSourceTexts.get(getJsonPathKey(fieldPath));
596
+ const serializedValue = serializeJsonValue(fieldValue, fieldNameSourceTexts, fieldPath);
597
+ if (!fieldNameSourceText || serializedValue === null) {
598
+ return null;
599
+ }
600
+ serializedFields.push(`${fieldNameSourceText}:${serializedValue}`);
601
+ }
602
+ return `{${serializedFields.join(",")}}`;
603
+ }
604
+ return JSON.stringify(jsonValue);
646
605
  }
647
606
  function isStringArray(value) {
648
- return Array.isArray(value) && value.every((item) => typeof item === "string");
649
- }
650
- function sortObjectKeysByOrder(record, order) {
651
- const orderIndex = new Map(order.map((key, index) => [key, index]));
652
- const known = [];
653
- const rest = [];
654
- for (const entry of Object.entries(record)) {
655
- const [key] = entry;
656
- if (orderIndex.has(key)) {
657
- known.push(entry);
658
- } else {
659
- rest.push(entry);
607
+ return Array.isArray(value) && value.every(isJsonStringValue);
608
+ }
609
+ function deduplicateStringValues(stringValues) {
610
+ const uniqueStringValues = new Map;
611
+ for (const stringValue of stringValues) {
612
+ const text = getJsonString(stringValue);
613
+ if (!uniqueStringValues.has(text)) {
614
+ uniqueStringValues.set(text, stringValue);
615
+ }
616
+ }
617
+ return [...uniqueStringValues.values()];
618
+ }
619
+ function sortJsonObject(jsonObject, compareKeys = compareText) {
620
+ return Object.fromEntries(Object.entries(jsonObject).sort(([leftKey], [rightKey]) => compareKeys(leftKey, rightKey)));
621
+ }
622
+ function sortJsonObjectByKeyOrder(jsonObject, keyOrder) {
623
+ const keyOrderIndexes = new Map(keyOrder.map((key, index) => [key, index]));
624
+ return sortJsonObject(jsonObject, (leftKey, rightKey) => {
625
+ const leftIndex = keyOrderIndexes.get(leftKey);
626
+ const rightIndex = keyOrderIndexes.get(rightKey);
627
+ const isLeftKeyKnown = leftIndex !== undefined;
628
+ const isRightKeyKnown = rightIndex !== undefined;
629
+ if (isLeftKeyKnown && isRightKeyKnown) {
630
+ return leftIndex - rightIndex;
631
+ }
632
+ if (isLeftKeyKnown) {
633
+ return -1;
634
+ }
635
+ if (isRightKeyKnown) {
636
+ return 1;
637
+ }
638
+ return compareText(leftKey, rightKey);
639
+ });
640
+ }
641
+ function sortJsonObjectRecursively(jsonObject, sortObject = sortJsonObject) {
642
+ const sortedNestedValues = Object.fromEntries(Object.entries(jsonObject).map(([key, value]) => [
643
+ key,
644
+ isJsonObject(value) ? sortJsonObjectRecursively(value, sortObject) : value
645
+ ]));
646
+ return sortObject(sortedNestedValues);
647
+ }
648
+ function sortJsonObjectValue(fieldValue, sortObject) {
649
+ return isJsonObject(fieldValue) ? sortObject(fieldValue) : fieldValue;
650
+ }
651
+ function sortJsonObjectValueAlphabetically(fieldValue) {
652
+ return sortJsonObjectValue(fieldValue, sortJsonObject);
653
+ }
654
+ function sortJsonObjectValueRecursively(fieldValue) {
655
+ return sortJsonObjectValue(fieldValue, sortJsonObjectRecursively);
656
+ }
657
+ function sortJsonObjectValueByKeyOrder(fieldValue, keyOrder) {
658
+ return sortJsonObjectValue(fieldValue, (jsonObject) => sortJsonObjectByKeyOrder(jsonObject, keyOrder));
659
+ }
660
+ function sortUniqueStringArrayValue(fieldValue) {
661
+ if (!isStringArray(fieldValue)) {
662
+ return fieldValue;
663
+ }
664
+ return deduplicateStringValues(fieldValue).sort((leftValue, rightValue) => compareText(getJsonString(leftValue), getJsonString(rightValue)));
665
+ }
666
+ function deduplicateStringArrayValue(fieldValue) {
667
+ return isStringArray(fieldValue) ? deduplicateStringValues(fieldValue) : fieldValue;
668
+ }
669
+ function sortPersonValue(fieldValue) {
670
+ return sortJsonObjectValueByKeyOrder(fieldValue, ["name", "email", "url"]);
671
+ }
672
+ function sortPeopleArrayValue(fieldValue) {
673
+ if (!Array.isArray(fieldValue)) {
674
+ return fieldValue;
675
+ }
676
+ return fieldValue.map((person) => sortPersonValue(person));
677
+ }
678
+ function sortPackageJsonFields(packageJson) {
679
+ const fieldOrderIndexes = new Map(PACKAGE_JSON_FIELD_ORDER.map((fieldName, index) => [fieldName, index]));
680
+ return sortJsonObject(packageJson, (leftFieldName, rightFieldName) => {
681
+ const leftIndex = fieldOrderIndexes.get(leftFieldName);
682
+ const rightIndex = fieldOrderIndexes.get(rightFieldName);
683
+ const isLeftFieldKnown = leftIndex !== undefined;
684
+ const isRightFieldKnown = rightIndex !== undefined;
685
+ if (isLeftFieldKnown && isRightFieldKnown) {
686
+ return leftIndex - rightIndex;
687
+ }
688
+ if (isLeftFieldKnown) {
689
+ return -1;
690
+ }
691
+ if (isRightFieldKnown) {
692
+ return 1;
693
+ }
694
+ const isLeftFieldPrivate = leftFieldName.startsWith("_");
695
+ const isRightFieldPrivate = rightFieldName.startsWith("_");
696
+ if (isLeftFieldPrivate !== isRightFieldPrivate) {
697
+ return isLeftFieldPrivate ? 1 : -1;
698
+ }
699
+ return compareText(leftFieldName, rightFieldName);
700
+ });
701
+ }
702
+ function isNpmDependencyOrderPreferred(packageJson) {
703
+ const packageManager = getJsonString(packageJson.packageManager);
704
+ if (packageManager !== null) {
705
+ return packageManager.startsWith("npm@");
706
+ }
707
+ const devEngines = packageJson.devEngines;
708
+ if (isJsonObject(devEngines)) {
709
+ const devPackageManager = devEngines.packageManager;
710
+ const devPackageManagerName = isJsonObject(devPackageManager) ? getJsonString(devPackageManager.name) : null;
711
+ if (devPackageManagerName !== null) {
712
+ return devPackageManagerName === "npm";
713
+ }
714
+ }
715
+ if (isJsonObject(packageJson.pnpm)) {
716
+ return false;
717
+ }
718
+ return true;
719
+ }
720
+ function sortDependencyObject(dependencyObject, packageJson) {
721
+ if (!isNpmDependencyOrderPreferred(packageJson)) {
722
+ return sortJsonObject(dependencyObject);
723
+ }
724
+ return sortJsonObject(dependencyObject, (leftName, rightName) => leftName.localeCompare(rightName, "en"));
725
+ }
726
+ function sortDependencyValue(fieldValue, packageJson) {
727
+ return sortJsonObjectValue(fieldValue, (dependencyObject) => sortDependencyObject(dependencyObject, packageJson));
728
+ }
729
+ function sortWorkspacesValue(fieldValue, packageJson) {
730
+ if (!isJsonObject(fieldValue)) {
731
+ return fieldValue;
732
+ }
733
+ const sortedWorkspaces = sortJsonObjectByKeyOrder(fieldValue, [
734
+ "packages",
735
+ "catalog"
736
+ ]);
737
+ const workspacePackages = sortedWorkspaces.packages;
738
+ if (workspacePackages !== undefined) {
739
+ sortedWorkspaces.packages = sortUniqueStringArrayValue(workspacePackages);
740
+ }
741
+ if (isJsonObject(sortedWorkspaces.catalog)) {
742
+ sortedWorkspaces.catalog = sortDependencyObject(sortedWorkspaces.catalog, packageJson);
743
+ }
744
+ return sortedWorkspaces;
745
+ }
746
+ function sortEslintRules(rules) {
747
+ return sortJsonObject(rules, (leftRuleName, rightRuleName) => {
748
+ const leftPluginDepth = leftRuleName.split("/").length;
749
+ const rightPluginDepth = rightRuleName.split("/").length;
750
+ return leftPluginDepth - rightPluginDepth || leftRuleName.localeCompare(rightRuleName);
751
+ });
752
+ }
753
+ function sortEslintConfigObject(eslintConfig) {
754
+ const sortedConfig = sortJsonObjectByKeyOrder(eslintConfig, ESLINT_CONFIG_FIELD_ORDER);
755
+ for (const fieldName of ["env", "globals", "parserOptions", "settings"]) {
756
+ if (isJsonObject(sortedConfig[fieldName])) {
757
+ sortedConfig[fieldName] = sortJsonObject(sortedConfig[fieldName]);
660
758
  }
661
759
  }
662
- known.sort(([a], [b]) => (orderIndex.get(a) ?? 0) - (orderIndex.get(b) ?? 0));
663
- rest.sort(([a], [b]) => a.localeCompare(b, "en"));
664
- return Object.fromEntries([...known, ...rest]);
760
+ if (isJsonObject(sortedConfig.rules)) {
761
+ sortedConfig.rules = sortEslintRules(sortedConfig.rules);
762
+ }
763
+ if (Array.isArray(sortedConfig.overrides)) {
764
+ sortedConfig.overrides = sortedConfig.overrides.map((override) => isJsonObject(override) ? sortEslintConfigObject(override) : override);
765
+ }
766
+ return sortedConfig;
767
+ }
768
+ function sortEslintConfigValue(fieldValue) {
769
+ return sortJsonObjectValue(fieldValue, sortEslintConfigObject);
770
+ }
771
+ function sortPrettierConfigObject(prettierConfig) {
772
+ const keyOrder = Object.keys(prettierConfig).filter((key) => key !== "overrides").sort(compareText);
773
+ if (Object.hasOwn(prettierConfig, "overrides")) {
774
+ keyOrder.push("overrides");
775
+ }
776
+ const sortedConfig = sortJsonObjectByKeyOrder(prettierConfig, keyOrder);
777
+ if (Array.isArray(sortedConfig.overrides)) {
778
+ sortedConfig.overrides = sortedConfig.overrides.map((override) => {
779
+ if (!isJsonObject(override)) {
780
+ return override;
781
+ }
782
+ const sortedOverride = sortJsonObject(override);
783
+ if (isJsonObject(sortedOverride.options)) {
784
+ sortedOverride.options = sortJsonObject(sortedOverride.options);
785
+ }
786
+ return sortedOverride;
787
+ });
788
+ }
789
+ return sortedConfig;
790
+ }
791
+ function sortPrettierConfigValue(fieldValue) {
792
+ return sortJsonObjectValue(fieldValue, sortPrettierConfigObject);
665
793
  }
666
- function sortObjectKeysAlpha(value) {
667
- if (!isPlainObject(value)) {
668
- return value;
794
+ function sortWireitScriptObject(scriptConfig) {
795
+ const sortedConfig = sortJsonObjectByKeyOrder(scriptConfig, WIREIT_SCRIPT_FIELD_ORDER);
796
+ if (Array.isArray(sortedConfig.dependencies)) {
797
+ sortedConfig.dependencies = sortedConfig.dependencies.map((dependency) => sortJsonObjectValueByKeyOrder(dependency, ["script", "cascade"]));
798
+ }
799
+ if (isJsonObject(sortedConfig.env)) {
800
+ sortedConfig.env = sortJsonObject(Object.fromEntries(Object.entries(sortedConfig.env).map(([name, value]) => [
801
+ name,
802
+ sortJsonObjectValueByKeyOrder(value, ["external", "default"])
803
+ ])));
804
+ }
805
+ if (isJsonObject(sortedConfig.service)) {
806
+ sortedConfig.service = sortJsonObjectByKeyOrder(sortedConfig.service, [
807
+ "readyWhen"
808
+ ]);
809
+ if (isJsonObject(sortedConfig.service.readyWhen)) {
810
+ sortedConfig.service.readyWhen = sortJsonObject(sortedConfig.service.readyWhen);
811
+ }
669
812
  }
670
- return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b, "en")));
813
+ return sortedConfig;
671
814
  }
672
- function sortStringArrayAlpha(value) {
673
- return [...value].sort((a, b) => a.localeCompare(b, "en"));
815
+ function sortWireitValue(fieldValue) {
816
+ if (!isJsonObject(fieldValue)) {
817
+ return fieldValue;
818
+ }
819
+ return sortJsonObject(Object.fromEntries(Object.entries(fieldValue).map(([scriptName, scriptConfig]) => [
820
+ scriptName,
821
+ sortJsonObjectValue(scriptConfig, sortWireitScriptObject)
822
+ ])));
674
823
  }
675
- function isPackageJson(filepath) {
676
- if (!filepath) {
824
+ function hasSequentialScript(packageJson) {
825
+ const devDependencies = packageJson.devDependencies;
826
+ if (!isJsonObject(devDependencies)) {
827
+ return false;
828
+ }
829
+ const isSequentialRunnerInstalled = Object.hasOwn(devDependencies, "npm-run-all") || Object.hasOwn(devDependencies, "npm-run-all2");
830
+ if (!isSequentialRunnerInstalled) {
677
831
  return false;
678
832
  }
679
- return path.basename(filepath) === "package.json";
833
+ return ["scripts", "betterScripts"].some((fieldName) => {
834
+ const scripts = packageJson[fieldName];
835
+ return isJsonObject(scripts) && Object.values(scripts).some((script) => {
836
+ const scriptText = getJsonString(script);
837
+ return scriptText !== null && scriptText.includes("*") && SEQUENTIAL_SCRIPT_PATTERN.test(scriptText);
838
+ });
839
+ });
840
+ }
841
+ function sortScriptNames(scriptNames, namespacePrefix = "") {
842
+ const scriptGroups = new Map;
843
+ for (const scriptName of scriptNames) {
844
+ const unprefixedScriptName = namespacePrefix ? scriptName.slice(namespacePrefix.length + 1) : scriptName;
845
+ const namespaceSeparatorIndex = unprefixedScriptName.indexOf(":");
846
+ const scriptGroupName = namespaceSeparatorIndex > 0 ? scriptName.slice(0, (namespacePrefix ? namespacePrefix.length + 1 : 0) + namespaceSeparatorIndex) : scriptName;
847
+ const groupedScriptNames = scriptGroups.get(scriptGroupName) ?? [];
848
+ groupedScriptNames.push(scriptName);
849
+ scriptGroups.set(scriptGroupName, groupedScriptNames);
850
+ }
851
+ return [...scriptGroups.keys()].sort(compareText).flatMap((scriptGroupName) => {
852
+ const groupedScriptNames = scriptGroups.get(scriptGroupName);
853
+ const isNestedGroup = groupedScriptNames.length > 1 && groupedScriptNames.some((scriptName) => scriptName !== scriptGroupName && scriptName.startsWith(`${scriptGroupName}:`));
854
+ if (!isNestedGroup) {
855
+ return groupedScriptNames.sort(compareText);
856
+ }
857
+ const directScriptNames = groupedScriptNames.filter((scriptName) => scriptName === scriptGroupName || !scriptName.startsWith(`${scriptGroupName}:`)).sort(compareText);
858
+ const nestedScriptNames = groupedScriptNames.filter((scriptName) => scriptName.startsWith(`${scriptGroupName}:`));
859
+ return [
860
+ ...directScriptNames,
861
+ ...sortScriptNames(nestedScriptNames, scriptGroupName)
862
+ ];
863
+ });
864
+ }
865
+ function sortScriptsValue(fieldValue, packageJson) {
866
+ if (!isJsonObject(fieldValue)) {
867
+ return fieldValue;
868
+ }
869
+ const scriptNames = Object.keys(fieldValue);
870
+ const baseScriptNames = new Set;
871
+ const normalizedScriptNames = scriptNames.map((scriptName) => {
872
+ const baseScriptName = scriptName.replace(/^(?:pre|post)/, "");
873
+ const isLifecycleScript = NPM_LIFECYCLE_SCRIPT_NAMES.has(baseScriptName);
874
+ const isBaseScriptPresent = scriptNames.includes(baseScriptName);
875
+ if (isLifecycleScript || isBaseScriptPresent) {
876
+ baseScriptNames.add(baseScriptName);
877
+ return baseScriptName;
878
+ }
879
+ return scriptName;
880
+ });
881
+ const orderedBaseScriptNames = hasSequentialScript(packageJson) ? [...new Set(normalizedScriptNames)] : sortScriptNames(normalizedScriptNames);
882
+ const orderedScriptNames = orderedBaseScriptNames.flatMap((baseScriptName) => baseScriptNames.has(baseScriptName) ? [`pre${baseScriptName}`, baseScriptName, `post${baseScriptName}`] : [baseScriptName]);
883
+ return Object.fromEntries(orderedScriptNames.filter((scriptName) => Object.hasOwn(fieldValue, scriptName)).map((scriptName) => [scriptName, fieldValue[scriptName]]));
884
+ }
885
+ function sortExportsValue(fieldValue) {
886
+ if (!isJsonObject(fieldValue)) {
887
+ return fieldValue;
888
+ }
889
+ const exportKeys = Object.keys(fieldValue);
890
+ const exportPathKeys = exportKeys.filter((key) => key.startsWith("."));
891
+ const exportConditionKeys = exportKeys.filter((key) => !key.startsWith(".") && key !== "default");
892
+ if (Object.hasOwn(fieldValue, "default")) {
893
+ exportConditionKeys.push("default");
894
+ }
895
+ return Object.fromEntries([...exportPathKeys, ...exportConditionKeys].map((exportKey) => [exportKey, sortExportsValue(fieldValue[exportKey])]));
680
896
  }
681
- function detectIndent(source) {
682
- const match = /\n([ \t]+)\S/.exec(source);
683
- return match ? match[1] ?? " " : " ";
897
+ function sortDevEnginesValue(fieldValue) {
898
+ if (!isJsonObject(fieldValue) || !isJsonObject(fieldValue.packageManager)) {
899
+ return fieldValue;
900
+ }
901
+ return {
902
+ ...fieldValue,
903
+ packageManager: sortJsonObjectByKeyOrder(fieldValue.packageManager, [
904
+ "name",
905
+ "version",
906
+ "onFail"
907
+ ])
908
+ };
909
+ }
910
+ function getDependencyName(dependencyIdentifier) {
911
+ const versionSeparatorIndex = dependencyIdentifier.indexOf("@", dependencyIdentifier.startsWith("@") ? 1 : 0);
912
+ return versionSeparatorIndex < 0 ? dependencyIdentifier : dependencyIdentifier.slice(0, versionSeparatorIndex);
684
913
  }
685
- function sortPackageJson(text, rawOptions) {
686
- if (!isPackageJson(rawOptions.filepath)) {
687
- return text;
914
+ function parsePackageSelector(packageSelector) {
915
+ const [nameAndVersion = packageSelector] = packageSelector.split(">");
916
+ const versionSeparatorIndex = nameAndVersion.lastIndexOf("@");
917
+ if (versionSeparatorIndex <= 0) {
918
+ return { name: packageSelector, versionRange: null };
688
919
  }
689
- const options2 = resolveSortOptions(rawOptions);
690
- const exclude = new Set(options2.packageJsonOrderExcludeKeys);
691
- let parsed;
920
+ return {
921
+ name: nameAndVersion.slice(0, versionSeparatorIndex),
922
+ versionRange: nameAndVersion.slice(versionSeparatorIndex + 1) || null
923
+ };
924
+ }
925
+ function getMinimumSemanticVersion(versionRange) {
692
926
  try {
693
- parsed = JSON.parse(text);
927
+ return findMinimumSemanticVersion(versionRange);
694
928
  } catch {
695
- return text;
929
+ return null;
696
930
  }
697
- if (!isPlainObject(parsed)) {
698
- return text;
931
+ }
932
+ function comparePnpmOverrideSelectors(leftSelector, rightSelector) {
933
+ const leftPackage = parsePackageSelector(leftSelector);
934
+ const rightPackage = parsePackageSelector(rightSelector);
935
+ if (leftPackage.name !== rightPackage.name) {
936
+ return leftPackage.name.localeCompare(rightPackage.name, "en");
937
+ }
938
+ if (!leftPackage.versionRange && !rightPackage.versionRange) {
939
+ return 0;
940
+ }
941
+ if (!leftPackage.versionRange) {
942
+ return -1;
943
+ }
944
+ if (!rightPackage.versionRange) {
945
+ return 1;
946
+ }
947
+ const leftVersion = getMinimumSemanticVersion(leftPackage.versionRange);
948
+ const rightVersion = getMinimumSemanticVersion(rightPackage.versionRange);
949
+ if (!leftVersion && !rightVersion) {
950
+ return compareText(leftPackage.versionRange, rightPackage.versionRange);
951
+ }
952
+ if (!leftVersion) {
953
+ return 1;
954
+ }
955
+ if (!rightVersion) {
956
+ return -1;
957
+ }
958
+ return leftVersion.compare(rightVersion);
959
+ }
960
+ function sortDependencyMetadataValue(fieldValue) {
961
+ return sortJsonObjectValue(fieldValue, (dependencyMetadata) => {
962
+ const compareDependencyIdentifiers = (leftIdentifier, rightIdentifier) => compareText(getDependencyName(leftIdentifier), getDependencyName(rightIdentifier));
963
+ return sortJsonObjectRecursively(dependencyMetadata, (jsonObject) => sortJsonObject(jsonObject, compareDependencyIdentifiers));
964
+ });
965
+ }
966
+ function sortPnpmConfigValue(fieldValue) {
967
+ return sortJsonObjectValue(fieldValue, (pnpmConfig) => {
968
+ const sortedPnpmConfig = sortJsonObjectRecursively(pnpmConfig, (jsonObject) => sortJsonObjectByKeyOrder(jsonObject, PNPM_CONFIG_FIELD_ORDER));
969
+ if (!isJsonObject(sortedPnpmConfig.overrides)) {
970
+ return sortedPnpmConfig;
971
+ }
972
+ return {
973
+ ...sortedPnpmConfig,
974
+ overrides: sortJsonObject(sortedPnpmConfig.overrides, comparePnpmOverrideSelectors)
975
+ };
976
+ });
977
+ }
978
+ var PACKAGE_JSON_FIELD_SORTERS = {
979
+ categories: deduplicateStringArrayValue,
980
+ keywords: deduplicateStringArrayValue,
981
+ bugs: (fieldValue) => sortJsonObjectValueByKeyOrder(fieldValue, ["url", "email"]),
982
+ repository: (fieldValue) => sortJsonObjectValueByKeyOrder(fieldValue, ["type", "url"]),
983
+ funding: (fieldValue) => sortJsonObjectValueByKeyOrder(fieldValue, ["type", "url"]),
984
+ license: (fieldValue) => sortJsonObjectValueByKeyOrder(fieldValue, ["type", "url"]),
985
+ author: sortPersonValue,
986
+ maintainers: sortPeopleArrayValue,
987
+ contributors: sortPeopleArrayValue,
988
+ exports: sortExportsValue,
989
+ bin: sortJsonObjectValueAlphabetically,
990
+ directories: (fieldValue) => sortJsonObjectValueByKeyOrder(fieldValue, DIRECTORY_FIELD_ORDER),
991
+ files: deduplicateStringArrayValue,
992
+ workspaces: sortWorkspacesValue,
993
+ binary: (fieldValue) => sortJsonObjectValueByKeyOrder(fieldValue, [
994
+ "module_name",
995
+ "module_path",
996
+ "remote_path",
997
+ "package_name",
998
+ "host"
999
+ ]),
1000
+ scripts: sortScriptsValue,
1001
+ betterScripts: sortScriptsValue,
1002
+ wireit: sortWireitValue,
1003
+ contributes: sortJsonObjectValueAlphabetically,
1004
+ activationEvents: deduplicateStringArrayValue,
1005
+ husky: (fieldValue) => {
1006
+ if (!isJsonObject(fieldValue) || !isJsonObject(fieldValue.hooks)) {
1007
+ return fieldValue;
1008
+ }
1009
+ return {
1010
+ ...fieldValue,
1011
+ hooks: sortJsonObjectByKeyOrder(fieldValue.hooks, GIT_HOOK_ORDER)
1012
+ };
1013
+ },
1014
+ "simple-git-hooks": (fieldValue) => sortJsonObjectValueByKeyOrder(fieldValue, GIT_HOOK_ORDER),
1015
+ commitlint: sortJsonObjectValueAlphabetically,
1016
+ config: sortJsonObjectValueAlphabetically,
1017
+ nodemonConfig: sortJsonObjectValueAlphabetically,
1018
+ browserify: sortJsonObjectValueAlphabetically,
1019
+ babel: sortJsonObjectValueAlphabetically,
1020
+ xo: sortJsonObjectValueAlphabetically,
1021
+ prettier: sortPrettierConfigValue,
1022
+ eslintConfig: sortEslintConfigValue,
1023
+ npmpkgjsonlint: sortJsonObjectValueAlphabetically,
1024
+ npmPackageJsonLintConfig: sortJsonObjectValueAlphabetically,
1025
+ npmpackagejsonlint: sortJsonObjectValueAlphabetically,
1026
+ release: sortJsonObjectValueAlphabetically,
1027
+ remarkConfig: sortJsonObjectValueAlphabetically,
1028
+ ava: sortJsonObjectValueAlphabetically,
1029
+ jest: sortJsonObjectValueAlphabetically,
1030
+ "jest-junit": sortJsonObjectValueAlphabetically,
1031
+ "jest-stare": sortJsonObjectValueAlphabetically,
1032
+ mocha: sortJsonObjectValueAlphabetically,
1033
+ nyc: sortJsonObjectValueAlphabetically,
1034
+ c8: sortJsonObjectValueAlphabetically,
1035
+ tap: sortJsonObjectValueAlphabetically,
1036
+ oclif: sortJsonObjectValueRecursively,
1037
+ resolutions: sortJsonObjectValueAlphabetically,
1038
+ overrides: sortDependencyValue,
1039
+ dependencies: sortDependencyValue,
1040
+ devDependencies: sortDependencyValue,
1041
+ dependenciesMeta: sortDependencyMetadataValue,
1042
+ peerDependencies: sortDependencyValue,
1043
+ peerDependenciesMeta: sortJsonObjectValueRecursively,
1044
+ optionalDependencies: sortDependencyValue,
1045
+ bundledDependencies: sortUniqueStringArrayValue,
1046
+ bundleDependencies: sortUniqueStringArrayValue,
1047
+ extensionPack: sortUniqueStringArrayValue,
1048
+ extensionDependencies: sortUniqueStringArrayValue,
1049
+ engines: sortJsonObjectValueAlphabetically,
1050
+ engineStrict: sortJsonObjectValueAlphabetically,
1051
+ devEngines: sortDevEnginesValue,
1052
+ volta: (fieldValue) => sortJsonObjectValueByKeyOrder(fieldValue, ["node", "npm", "yarn"]),
1053
+ preferGlobal: sortJsonObjectValueAlphabetically,
1054
+ publishConfig: sortJsonObjectValueAlphabetically,
1055
+ badges: (fieldValue) => {
1056
+ if (!Array.isArray(fieldValue)) {
1057
+ return fieldValue;
1058
+ }
1059
+ return fieldValue.map((badge) => sortJsonObjectValueByKeyOrder(badge, ["description", "url", "href"]));
1060
+ },
1061
+ galleryBanner: sortJsonObjectValueAlphabetically,
1062
+ pnpm: sortPnpmConfigValue
1063
+ };
1064
+ function sortPackageJsonObject(packageJson) {
1065
+ const sortedPackageJsonFields = sortPackageJsonFields(packageJson);
1066
+ return Object.fromEntries(Object.entries(sortedPackageJsonFields).map(([fieldName, fieldValue]) => {
1067
+ const fieldSorter = Object.hasOwn(PACKAGE_JSON_FIELD_SORTERS, fieldName) ? PACKAGE_JSON_FIELD_SORTERS[fieldName] : undefined;
1068
+ return [
1069
+ fieldName,
1070
+ fieldSorter ? fieldSorter(fieldValue, packageJson) : fieldValue
1071
+ ];
1072
+ }));
1073
+ }
1074
+ function isPackageJsonFile(filePath) {
1075
+ return filePath?.split(/[\\/]/).at(-1) === "package.json";
1076
+ }
1077
+ async function preprocessPackageJson(sourceText, prettierOptions, parser) {
1078
+ if (!isPackageJsonFile(prettierOptions.filepath) || !resolveSortOptions(prettierOptions).packageSort) {
1079
+ return sourceText;
699
1080
  }
700
- let result = parsed;
701
- for (const field of DEPENDENCY_FIELDS) {
702
- const dependencyMap = result[field];
703
- if (dependencyMap !== undefined && !exclude.has(field)) {
704
- result[field] = sortObjectKeysAlpha(dependencyMap);
1081
+ try {
1082
+ const parsedPackageJson = JSON.parse(sourceText);
1083
+ if (!isJsonObject(parsedPackageJson)) {
1084
+ return sourceText;
1085
+ }
1086
+ const parserJsonAst = await parser.parse(sourceText, prettierOptions);
1087
+ if (!isParserJsonAstNode(parserJsonAst)) {
1088
+ return sourceText;
705
1089
  }
1090
+ const fieldNameSourceTexts = new Map;
1091
+ const packageJsonWithSourceLiterals = preserveJsonSourceLiterals(parsedPackageJson, parserJsonAst, sourceText, fieldNameSourceTexts);
1092
+ if (!isJsonObject(packageJsonWithSourceLiterals)) {
1093
+ return sourceText;
1094
+ }
1095
+ return serializeJsonValue(sortPackageJsonObject(packageJsonWithSourceLiterals), fieldNameSourceTexts) ?? sourceText;
1096
+ } catch {
1097
+ return sourceText;
706
1098
  }
707
- if (options2.packageJsonOrder) {
708
- result = sortObjectKeysByOrder(result, PACKAGE_JSON_TOP_LEVEL_ORDER);
709
- for (const [key, value] of Object.entries(result)) {
710
- if (exclude.has(key)) {
711
- continue;
1099
+ }
1100
+
1101
+ // src/sort-exports.ts
1102
+ function buildExportSortingEdits(sourceText, programStatements, sortedComments) {
1103
+ const sortingEdits = [];
1104
+ for (const exportDeclarationNode of programStatements) {
1105
+ if (exportDeclarationNode.type !== "ExportNamedDeclaration" || exportDeclarationNode.declaration) {
1106
+ continue;
1107
+ }
1108
+ const declarationRange = getAstNodeTextRange(exportDeclarationNode);
1109
+ const specifierNodes = exportDeclarationNode.specifiers;
1110
+ if (!declarationRange || !specifierNodes || specifierNodes.length <= 1 || isPrettierIgnored(sourceText, declarationRange, sortedComments) || specifierNodes.some((specifierNode) => specifierNode.type !== "ExportSpecifier")) {
1111
+ continue;
1112
+ }
1113
+ const firstSpecifierRange = getAstNodeTextRange(specifierNodes[0]);
1114
+ const lastSpecifierRange = getAstNodeTextRange(specifierNodes.at(-1));
1115
+ if (!firstSpecifierRange || !lastSpecifierRange) {
1116
+ continue;
1117
+ }
1118
+ const openingBraceIndex = sourceText.lastIndexOf("{", firstSpecifierRange.start);
1119
+ const closingBraceIndex = sourceText.indexOf("}", lastSpecifierRange.end);
1120
+ if (openingBraceIndex < declarationRange.start || closingBraceIndex < lastSpecifierRange.end || closingBraceIndex >= declarationRange.end) {
1121
+ continue;
1122
+ }
1123
+ const firstInternalCommentIndex = findCommentIndexAtOrAfter(sortedComments, openingBraceIndex + 1);
1124
+ const firstInternalComment = sortedComments[firstInternalCommentIndex];
1125
+ const isInternalCommentPresent = firstInternalComment !== undefined && firstInternalComment.textRange.start < closingBraceIndex;
1126
+ if (isInternalCommentPresent) {
1127
+ continue;
1128
+ }
1129
+ let isEverySpecifierValid = true;
1130
+ const sortableSpecifiers = [];
1131
+ for (const [originalIndex, specifierNode] of specifierNodes.entries()) {
1132
+ const specifierRange = getAstNodeTextRange(specifierNode);
1133
+ const exportedName = getAstNodeName(specifierNode.exported);
1134
+ const localName = getAstNodeName(specifierNode.local);
1135
+ const sortName = exportedName ?? localName;
1136
+ if (!specifierRange || !sortName) {
1137
+ isEverySpecifierValid = false;
1138
+ break;
712
1139
  }
713
- if (isStringArray(value)) {
714
- result[key] = sortStringArrayAlpha(value);
1140
+ sortableSpecifiers.push({
1141
+ originalIndex,
1142
+ sortName,
1143
+ specifierText: sourceText.slice(specifierRange.start, specifierRange.end)
1144
+ });
1145
+ }
1146
+ if (!isEverySpecifierValid) {
1147
+ continue;
1148
+ }
1149
+ const sortedSpecifiers = [...sortableSpecifiers].sort((left, right) => left.sortName.localeCompare(right.sortName, "en", {
1150
+ sensitivity: "base"
1151
+ }) || left.originalIndex - right.originalIndex);
1152
+ const isSpecifierOrderUnchanged = sortedSpecifiers.every((specifier, sortedIndex) => specifier.originalIndex === sortedIndex);
1153
+ if (isSpecifierOrderUnchanged) {
1154
+ continue;
1155
+ }
1156
+ sortingEdits.push({
1157
+ start: openingBraceIndex,
1158
+ end: closingBraceIndex + 1,
1159
+ replacementText: `{ ${sortedSpecifiers.map((specifier) => specifier.specifierText).join(", ")} }`
1160
+ });
1161
+ }
1162
+ return sortingEdits;
1163
+ }
1164
+
1165
+ // src/utils/source-text.ts
1166
+ function applySourceTextEdits(sourceText, sourceTextEdits) {
1167
+ if (sourceTextEdits.length === 0) {
1168
+ return sourceText;
1169
+ }
1170
+ let previousEditEnd = 0;
1171
+ const ascendingEdits = [...sourceTextEdits].sort((left, right) => left.start - right.start || left.end - right.end);
1172
+ for (const sourceTextEdit of ascendingEdits) {
1173
+ const { start, end } = sourceTextEdit;
1174
+ const isSourceTextEditValid = [start, end].every(Number.isSafeInteger) && start >= previousEditEnd && end >= start && end <= sourceText.length;
1175
+ if (!isSourceTextEditValid) {
1176
+ return null;
1177
+ }
1178
+ previousEditEnd = end;
1179
+ }
1180
+ let editedText = sourceText;
1181
+ for (const sourceTextEdit of ascendingEdits.reverse()) {
1182
+ editedText = editedText.slice(0, sourceTextEdit.start) + sourceTextEdit.replacementText + editedText.slice(sourceTextEdit.end);
1183
+ }
1184
+ return editedText;
1185
+ }
1186
+
1187
+ // src/sort-imports.ts
1188
+ var INDEX_MODULE_PATTERN = /^\.\/index(?:\.[^/]+)*$/;
1189
+ var PRETTIER_FILE_PRAGMA_DIRECTIVES = ["@format", "@prettier"];
1190
+ var ESLINT_RANGE_DIRECTIVES = ["eslint-disable", "eslint-enable"];
1191
+ var ESLINT_GLOBAL_DIRECTIVES = ["global", "globals"];
1192
+ function getCommentDirectiveArguments(commentLine, directive) {
1193
+ if (!commentLine.startsWith(directive)) {
1194
+ return null;
1195
+ }
1196
+ const directiveArguments = commentLine.slice(directive.length);
1197
+ if (directiveArguments === "") {
1198
+ return "";
1199
+ }
1200
+ if (directiveArguments[0]?.trim() !== "") {
1201
+ return null;
1202
+ }
1203
+ return directiveArguments.trimStart();
1204
+ }
1205
+ function hasCommentDirective(commentLine, directives) {
1206
+ return directives.some((directive) => getCommentDirectiveArguments(commentLine, directive) !== null);
1207
+ }
1208
+ function isEslintRuleConfiguration(commentLine) {
1209
+ const ruleConfiguration = getCommentDirectiveArguments(commentLine, "eslint");
1210
+ if (!ruleConfiguration) {
1211
+ return false;
1212
+ }
1213
+ const separatorIndex = ruleConfiguration.indexOf(":");
1214
+ if (separatorIndex < 1) {
1215
+ return false;
1216
+ }
1217
+ const ruleName = ruleConfiguration.slice(0, separatorIndex).trimEnd();
1218
+ return ruleName.length > 0 && [...ruleName].every((character) => character.trim() !== "");
1219
+ }
1220
+ function isFixedEslintComment(comment, isBlockComment) {
1221
+ if (!isBlockComment) {
1222
+ return false;
1223
+ }
1224
+ const commentText = getAstCommentText(comment)?.trim();
1225
+ if (!commentText) {
1226
+ return false;
1227
+ }
1228
+ return hasCommentDirective(commentText, ESLINT_RANGE_DIRECTIVES) || isEslintRuleConfiguration(commentText) || hasCommentDirective(commentText, ESLINT_GLOBAL_DIRECTIVES);
1229
+ }
1230
+ function isEslintNextLineComment(comment) {
1231
+ const commentText = getAstCommentText(comment)?.trim();
1232
+ if (!commentText) {
1233
+ return false;
1234
+ }
1235
+ return getCommentDirectiveArguments(commentText, "eslint-disable-next-line") !== null;
1236
+ }
1237
+ function isFixedEslintParserComment(sourceText, parserComment) {
1238
+ const { comment, textRange } = parserComment;
1239
+ const sourceCommentText = sourceText.slice(textRange.start, textRange.end);
1240
+ return isFixedEslintComment(comment, sourceCommentText.startsWith("/*"));
1241
+ }
1242
+ function isPositionSensitiveEslintComment(sourceText, parserComment) {
1243
+ return isEslintNextLineComment(parserComment.comment) || isFixedEslintParserComment(sourceText, parserComment);
1244
+ }
1245
+ function getCommentLines(comment) {
1246
+ const commentText = getAstCommentText(comment);
1247
+ if (commentText === null) {
1248
+ return [];
1249
+ }
1250
+ return commentText.split(/\r?\n/).map((commentLine) => commentLine.replace(/^\s*\*+\s*/, ""));
1251
+ }
1252
+ function isFixedFileComment(sourceText, comment, commentRange, prettierFilePragmaComment) {
1253
+ const sourceCommentText = sourceText.slice(commentRange.start, commentRange.end);
1254
+ if (sourceCommentText.startsWith("#!")) {
1255
+ return true;
1256
+ }
1257
+ if (comment === prettierFilePragmaComment) {
1258
+ return true;
1259
+ }
1260
+ const isBlockComment = sourceCommentText.startsWith("/*");
1261
+ return isFixedEslintComment(comment, isBlockComment);
1262
+ }
1263
+ function getPrettierFilePragmaComment(sortedComments, isPrettierFilePragmaPresent) {
1264
+ if (!isPrettierFilePragmaPresent) {
1265
+ return null;
1266
+ }
1267
+ return sortedComments.find(({ comment }) => getCommentLines(comment).some((commentLine) => hasCommentDirective(commentLine.trim(), PRETTIER_FILE_PRAGMA_DIRECTIVES)))?.comment ?? null;
1268
+ }
1269
+ function classifyImportGroup(moduleSpecifier) {
1270
+ if (moduleSpecifier === "bun" || moduleSpecifier.startsWith("bun:") || moduleSpecifier.startsWith("node:")) {
1271
+ return "builtin";
1272
+ }
1273
+ if (moduleSpecifier === "." || moduleSpecifier === "./" || INDEX_MODULE_PATTERN.test(moduleSpecifier)) {
1274
+ return "index";
1275
+ }
1276
+ if (moduleSpecifier === ".." || moduleSpecifier.startsWith("../")) {
1277
+ return "parent";
1278
+ }
1279
+ if (moduleSpecifier.startsWith("./")) {
1280
+ return "sibling";
1281
+ }
1282
+ if (moduleSpecifier.startsWith("/") || moduleSpecifier.startsWith("~") || moduleSpecifier.startsWith("@/") || moduleSpecifier.startsWith("#")) {
1283
+ return "internal";
1284
+ }
1285
+ return "external";
1286
+ }
1287
+ function findLeadingCommentsStart(sourceText, statementStart, sortedComments, prettierFilePragmaComment) {
1288
+ let attachedTextStart = statementStart;
1289
+ for (let commentIndex = findCommentIndexAtOrAfter(sortedComments, statementStart) - 1;commentIndex >= 0; commentIndex--) {
1290
+ const parserComment = sortedComments[commentIndex];
1291
+ if (!parserComment || parserComment.textRange.end > statementStart) {
1292
+ continue;
1293
+ }
1294
+ const { comment, textRange } = parserComment;
1295
+ if (isFixedFileComment(sourceText, comment, textRange, prettierFilePragmaComment)) {
1296
+ break;
1297
+ }
1298
+ const textBetweenCommentAndImport = sourceText.slice(textRange.end, attachedTextStart);
1299
+ const lineBreakCount = textBetweenCommentAndImport.match(/\n/g)?.length ?? 0;
1300
+ const isEslintNextLineCommentAttached = attachedTextStart === statementStart && isEslintNextLineComment(comment) && textBetweenCommentAndImport.trim() === "" && lineBreakCount === 1;
1301
+ if (isEslintNextLineCommentAttached) {
1302
+ attachedTextStart = textRange.start;
1303
+ continue;
1304
+ }
1305
+ const commentLineStart = sourceText.lastIndexOf(`
1306
+ `, textRange.start - 1) + 1;
1307
+ if (sourceText.slice(commentLineStart, textRange.start).trim() !== "") {
1308
+ break;
1309
+ }
1310
+ if (textBetweenCommentAndImport.trim() !== "" || lineBreakCount >= 2) {
1311
+ break;
1312
+ }
1313
+ attachedTextStart = textRange.start;
1314
+ }
1315
+ while (attachedTextStart > 0 && /[ \t]/.test(sourceText[attachedTextStart - 1])) {
1316
+ attachedTextStart--;
1317
+ }
1318
+ return attachedTextStart;
1319
+ }
1320
+ function getRangeEndIncludingLineBreak(sourceText, sourceIndex) {
1321
+ if (sourceText[sourceIndex] === "\r" && sourceText[sourceIndex + 1] === `
1322
+ `) {
1323
+ return sourceIndex + 2;
1324
+ }
1325
+ if (sourceText[sourceIndex] === `
1326
+ `) {
1327
+ return sourceIndex + 1;
1328
+ }
1329
+ return sourceIndex;
1330
+ }
1331
+ function getTrailingComments(sourceText, statementEnd, sortedComments) {
1332
+ let trailingCommentsEnd = statementEnd;
1333
+ let isImportSortingBoundary = false;
1334
+ const firstTrailingCommentIndex = findCommentIndexAtOrAfter(sortedComments, statementEnd);
1335
+ for (let commentIndex = firstTrailingCommentIndex;commentIndex < sortedComments.length; commentIndex++) {
1336
+ const trailingComment = sortedComments[commentIndex];
1337
+ if (!trailingComment) {
1338
+ break;
1339
+ }
1340
+ const { textRange } = trailingComment;
1341
+ const textBeforeComment = sourceText.slice(trailingCommentsEnd, textRange.start);
1342
+ if (textBeforeComment.includes(`
1343
+ `) || textBeforeComment.trim() !== "") {
1344
+ break;
1345
+ }
1346
+ const commentLineEnd = sourceText.indexOf(`
1347
+ `, textRange.end);
1348
+ if (!isSourceRangeWhitespaceOrComments(sourceText, {
1349
+ start: textRange.end,
1350
+ end: commentLineEnd < 0 ? sourceText.length : commentLineEnd
1351
+ }, sortedComments)) {
1352
+ break;
1353
+ }
1354
+ if (isFixedEslintParserComment(sourceText, trailingComment)) {
1355
+ isImportSortingBoundary = true;
1356
+ break;
1357
+ }
1358
+ if (isEslintNextLineComment(trailingComment.comment)) {
1359
+ break;
1360
+ }
1361
+ trailingCommentsEnd = textRange.end;
1362
+ }
1363
+ return {
1364
+ commentsText: sourceText.slice(statementEnd, trailingCommentsEnd),
1365
+ rangeEnd: trailingCommentsEnd,
1366
+ isImportSortingBoundary
1367
+ };
1368
+ }
1369
+ function parseImportAttributes(sourceText, importDeclarationNode) {
1370
+ const declarationRange = getAstNodeTextRange(importDeclarationNode);
1371
+ const moduleSpecifierRange = getAstNodeTextRange(importDeclarationNode.source);
1372
+ if (!declarationRange || !moduleSpecifierRange) {
1373
+ return { attributesText: null, isValid: false };
1374
+ }
1375
+ const importAttributesText = sourceText.slice(moduleSpecifierRange.end, declarationRange.end).replace(/;\s*$/, "").trim();
1376
+ if (importAttributesText === "") {
1377
+ return { attributesText: null, isValid: true };
1378
+ }
1379
+ return {
1380
+ attributesText: importAttributesText,
1381
+ isValid: /^(?:with|assert)\s*\{[\s\S]*\}$/.test(importAttributesText)
1382
+ };
1383
+ }
1384
+ function parseImportDeclaration(importDeclarationMetadata, sourceText) {
1385
+ const {
1386
+ importDeclarationNode,
1387
+ declarationRange,
1388
+ leadingCommentsText,
1389
+ trailingCommentsText,
1390
+ hasInternalComments
1391
+ } = importDeclarationMetadata;
1392
+ const moduleSpecifierNode = importDeclarationNode.source;
1393
+ const moduleSpecifierRange = getAstNodeTextRange(moduleSpecifierNode);
1394
+ const moduleSpecifier = getAstNodeName(moduleSpecifierNode);
1395
+ if (!moduleSpecifierRange || moduleSpecifier === null) {
1396
+ return null;
1397
+ }
1398
+ let importKind = "value";
1399
+ const declarationText = sourceText.slice(declarationRange.start, declarationRange.end);
1400
+ if (typeof importDeclarationNode.importKind === "string") {
1401
+ importKind = importDeclarationNode.importKind;
1402
+ }
1403
+ let specifierNodes = [];
1404
+ const isTypeOnly = importKind === "type";
1405
+ if (Array.isArray(importDeclarationNode.specifiers)) {
1406
+ specifierNodes = importDeclarationNode.specifiers;
1407
+ }
1408
+ let importPhase = null;
1409
+ const isSideEffectOnly = specifierNodes.length === 0 && importKind === "value";
1410
+ const parsedImportAttributes = parseImportAttributes(sourceText, importDeclarationNode);
1411
+ if (typeof importDeclarationNode.phase === "string") {
1412
+ importPhase = importDeclarationNode.phase;
1413
+ }
1414
+ let defaultBinding = null;
1415
+ let namespaceBinding = null;
1416
+ let isEverySpecifierSupported = true;
1417
+ const namedSpecifiers = [];
1418
+ for (const specifierNode of specifierNodes) {
1419
+ if (specifierNode.type === "ImportDefaultSpecifier") {
1420
+ defaultBinding = getAstNodeName(specifierNode.local);
1421
+ isEverySpecifierSupported &&= defaultBinding !== null;
1422
+ continue;
1423
+ }
1424
+ if (specifierNode.type === "ImportNamespaceSpecifier") {
1425
+ namespaceBinding = getAstNodeName(specifierNode.local);
1426
+ isEverySpecifierSupported &&= namespaceBinding !== null;
1427
+ continue;
1428
+ }
1429
+ if (specifierNode.type !== "ImportSpecifier") {
1430
+ isEverySpecifierSupported = false;
1431
+ continue;
1432
+ }
1433
+ let specifierKind = "value";
1434
+ const importedNode = specifierNode.imported;
1435
+ const importedNameRange = getAstNodeTextRange(importedNode);
1436
+ const importedName = getAstNodeName(importedNode);
1437
+ const localName = getAstNodeName(specifierNode.local);
1438
+ if (typeof specifierNode.importKind === "string") {
1439
+ specifierKind = specifierNode.importKind;
1440
+ }
1441
+ if (!importedNameRange || importedName === null || localName === null || specifierKind !== "value" && specifierKind !== "type") {
1442
+ isEverySpecifierSupported = false;
1443
+ continue;
1444
+ }
1445
+ namedSpecifiers.push({
1446
+ importedName,
1447
+ importedNameText: sourceText.slice(importedNameRange.start, importedNameRange.end),
1448
+ localName,
1449
+ isTypeOnly: specifierKind === "type"
1450
+ });
1451
+ }
1452
+ let parsedNamedSpecifiers = null;
1453
+ const isTypeClauseRewriteUnsupported = isTypeOnly && (defaultBinding !== null || namespaceBinding !== null);
1454
+ const isVerbatimRenderingRequired = specifierNodes.length === 0 || hasInternalComments || importPhase !== null || importKind !== "value" && importKind !== "type" || !isEverySpecifierSupported || !parsedImportAttributes.isValid || isTypeClauseRewriteUnsupported || isTypeOnly && parsedImportAttributes.attributesText !== null;
1455
+ if (namedSpecifiers.length > 0) {
1456
+ parsedNamedSpecifiers = namedSpecifiers;
1457
+ }
1458
+ let verbatimDeclaration = null;
1459
+ if (isVerbatimRenderingRequired) {
1460
+ verbatimDeclaration = declarationText;
1461
+ }
1462
+ return {
1463
+ moduleSpecifier,
1464
+ moduleSpecifierText: sourceText.slice(moduleSpecifierRange.start, moduleSpecifierRange.end),
1465
+ isTypeOnly,
1466
+ isSideEffectOnly,
1467
+ defaultBinding,
1468
+ namespaceBinding,
1469
+ namedSpecifiers: parsedNamedSpecifiers,
1470
+ importAttributes: parsedImportAttributes.attributesText,
1471
+ verbatimDeclaration,
1472
+ leadingCommentsText,
1473
+ trailingCommentsText,
1474
+ isMergeBoundary: leadingCommentsText.trim() !== "" || trailingCommentsText !== "" || hasInternalComments
1475
+ };
1476
+ }
1477
+ function renderNamedImportSpecifiers(namedSpecifiers) {
1478
+ return namedSpecifiers.map((importSpecifier) => {
1479
+ let renderedSpecifier = importSpecifier.importedNameText;
1480
+ const isAliasRequired = importSpecifier.importedNameText !== importSpecifier.importedName || importSpecifier.importedName !== importSpecifier.localName;
1481
+ if (isAliasRequired) {
1482
+ renderedSpecifier = `${importSpecifier.importedNameText} as ${importSpecifier.localName}`;
1483
+ }
1484
+ if (importSpecifier.isTypeOnly) {
1485
+ return `type ${renderedSpecifier}`;
1486
+ }
1487
+ return renderedSpecifier;
1488
+ }).join(", ");
1489
+ }
1490
+ function renderImportBindings(importDeclaration) {
1491
+ const renderedBindings = [];
1492
+ if (importDeclaration.defaultBinding) {
1493
+ renderedBindings.push(importDeclaration.defaultBinding);
1494
+ }
1495
+ if (importDeclaration.namespaceBinding) {
1496
+ renderedBindings.push(`* as ${importDeclaration.namespaceBinding}`);
1497
+ }
1498
+ if (importDeclaration.namedSpecifiers) {
1499
+ renderedBindings.push(`{ ${renderNamedImportSpecifiers(importDeclaration.namedSpecifiers)} }`);
1500
+ }
1501
+ return renderedBindings.join(", ");
1502
+ }
1503
+ function renderImportDeclaration(importDeclaration) {
1504
+ if (importDeclaration.verbatimDeclaration !== null) {
1505
+ return importDeclaration.leadingCommentsText + importDeclaration.verbatimDeclaration + importDeclaration.trailingCommentsText;
1506
+ }
1507
+ let attributesSuffix = "";
1508
+ if (importDeclaration.importAttributes) {
1509
+ attributesSuffix = ` ${importDeclaration.importAttributes}`;
1510
+ }
1511
+ let declarationText = `import ${renderImportBindings(importDeclaration)} from ${importDeclaration.moduleSpecifierText}${attributesSuffix};`;
1512
+ if (importDeclaration.isTypeOnly) {
1513
+ declarationText = `import type ${renderImportBindings(importDeclaration)} from ${importDeclaration.moduleSpecifierText}${attributesSuffix};`;
1514
+ }
1515
+ return importDeclaration.leadingCommentsText + declarationText + importDeclaration.trailingCommentsText;
1516
+ }
1517
+ function sortNamedImportSpecifiers(namedSpecifiers) {
1518
+ return [...namedSpecifiers].sort((left, right) => left.localName.localeCompare(right.localName, "en", {
1519
+ sensitivity: "base"
1520
+ }));
1521
+ }
1522
+ function getLocalBindingNames(importDeclaration) {
1523
+ const localBindingNames = [];
1524
+ if (importDeclaration.defaultBinding) {
1525
+ localBindingNames.push(importDeclaration.defaultBinding);
1526
+ }
1527
+ if (importDeclaration.namespaceBinding) {
1528
+ localBindingNames.push(importDeclaration.namespaceBinding);
1529
+ }
1530
+ if (importDeclaration.namedSpecifiers) {
1531
+ localBindingNames.push(...importDeclaration.namedSpecifiers.map((importSpecifier) => importSpecifier.localName));
1532
+ }
1533
+ return localBindingNames;
1534
+ }
1535
+ function normalizeNamedTypeOnlyImport(importDeclaration) {
1536
+ if (!importDeclaration.isTypeOnly || !importDeclaration.namedSpecifiers || importDeclaration.defaultBinding || importDeclaration.namespaceBinding || importDeclaration.verbatimDeclaration !== null) {
1537
+ return importDeclaration;
1538
+ }
1539
+ return {
1540
+ ...importDeclaration,
1541
+ isTypeOnly: false,
1542
+ namedSpecifiers: importDeclaration.namedSpecifiers.map((importSpecifier) => ({
1543
+ ...importSpecifier,
1544
+ isTypeOnly: true
1545
+ }))
1546
+ };
1547
+ }
1548
+ function getImportRequestKey(importDeclaration) {
1549
+ return `${importDeclaration.moduleSpecifier}\x00${importDeclaration.importAttributes ?? ""}`;
1550
+ }
1551
+ function isImportDeclarationMergeSafe(targetImportDeclaration, candidateImportDeclaration, bindingCounts) {
1552
+ if (targetImportDeclaration.verbatimDeclaration !== null || candidateImportDeclaration.verbatimDeclaration !== null || targetImportDeclaration.isSideEffectOnly || candidateImportDeclaration.isSideEffectOnly || targetImportDeclaration.moduleSpecifier !== candidateImportDeclaration.moduleSpecifier || targetImportDeclaration.importAttributes !== candidateImportDeclaration.importAttributes || targetImportDeclaration.isMergeBoundary || candidateImportDeclaration.isMergeBoundary) {
1553
+ return false;
1554
+ }
1555
+ if (bindingCounts.defaultBindingCount > 1 && (targetImportDeclaration.defaultBinding !== null || candidateImportDeclaration.defaultBinding !== null) || bindingCounts.namespaceBindingCount > 1 && (targetImportDeclaration.namespaceBinding !== null || candidateImportDeclaration.namespaceBinding !== null) || targetImportDeclaration.defaultBinding !== null && candidateImportDeclaration.defaultBinding !== null || targetImportDeclaration.namespaceBinding !== null && candidateImportDeclaration.namespaceBinding !== null) {
1556
+ return false;
1557
+ }
1558
+ const isAnyNamedSpecifierPresent = (targetImportDeclaration.namedSpecifiers?.length ?? 0) > 0 || (candidateImportDeclaration.namedSpecifiers?.length ?? 0) > 0;
1559
+ if (isAnyNamedSpecifierPresent && (targetImportDeclaration.namespaceBinding !== null || candidateImportDeclaration.namespaceBinding !== null)) {
1560
+ return false;
1561
+ }
1562
+ const usedLocalNames = new Set(getLocalBindingNames(targetImportDeclaration));
1563
+ return getLocalBindingNames(candidateImportDeclaration).every((localName) => !usedLocalNames.has(localName));
1564
+ }
1565
+ function mergeImportDeclarations(targetImportDeclaration, candidateImportDeclaration) {
1566
+ let namedSpecifiers = null;
1567
+ const mergedNamedSpecifiers = [
1568
+ ...targetImportDeclaration.namedSpecifiers ?? [],
1569
+ ...candidateImportDeclaration.namedSpecifiers ?? []
1570
+ ];
1571
+ if (mergedNamedSpecifiers.length > 0) {
1572
+ namedSpecifiers = mergedNamedSpecifiers;
1573
+ }
1574
+ return {
1575
+ moduleSpecifier: targetImportDeclaration.moduleSpecifier,
1576
+ moduleSpecifierText: targetImportDeclaration.moduleSpecifierText,
1577
+ isTypeOnly: false,
1578
+ isSideEffectOnly: false,
1579
+ defaultBinding: targetImportDeclaration.defaultBinding ?? candidateImportDeclaration.defaultBinding,
1580
+ namespaceBinding: targetImportDeclaration.namespaceBinding ?? candidateImportDeclaration.namespaceBinding,
1581
+ namedSpecifiers,
1582
+ importAttributes: targetImportDeclaration.importAttributes,
1583
+ verbatimDeclaration: null,
1584
+ leadingCommentsText: targetImportDeclaration.leadingCommentsText,
1585
+ trailingCommentsText: "",
1586
+ isMergeBoundary: false
1587
+ };
1588
+ }
1589
+ function mergeCompatibleImports(importDeclarations) {
1590
+ const bindingCountsByImportDeclaration = new Map;
1591
+ const currentBindingCountsByRequest = new Map;
1592
+ const minimumMergeTargetIndexByRequest = new Map;
1593
+ for (const importDeclaration of importDeclarations) {
1594
+ const importRequestKey = getImportRequestKey(importDeclaration);
1595
+ if (importDeclaration.isMergeBoundary) {
1596
+ currentBindingCountsByRequest.delete(importRequestKey);
1597
+ continue;
1598
+ }
1599
+ if (importDeclaration.verbatimDeclaration !== null || importDeclaration.isSideEffectOnly) {
1600
+ continue;
1601
+ }
1602
+ const bindingCounts = currentBindingCountsByRequest.get(importRequestKey) ?? {
1603
+ defaultBindingCount: 0,
1604
+ namespaceBindingCount: 0
1605
+ };
1606
+ if (importDeclaration.defaultBinding !== null) {
1607
+ bindingCounts.defaultBindingCount++;
1608
+ }
1609
+ if (importDeclaration.namespaceBinding !== null) {
1610
+ bindingCounts.namespaceBindingCount++;
1611
+ }
1612
+ currentBindingCountsByRequest.set(importRequestKey, bindingCounts);
1613
+ bindingCountsByImportDeclaration.set(importDeclaration, bindingCounts);
1614
+ }
1615
+ const mergedImportDeclarations = [];
1616
+ for (const originalImportDeclaration of importDeclarations) {
1617
+ const importDeclaration = normalizeNamedTypeOnlyImport(originalImportDeclaration);
1618
+ const importRequestKey = getImportRequestKey(importDeclaration);
1619
+ const bindingCounts = bindingCountsByImportDeclaration.get(originalImportDeclaration) ?? {
1620
+ defaultBindingCount: 0,
1621
+ namespaceBindingCount: 0
1622
+ };
1623
+ const minimumMergeTargetIndex = minimumMergeTargetIndexByRequest.get(importRequestKey) ?? 0;
1624
+ const mergeTargetIndex = mergedImportDeclarations.findIndex((existingImportDeclaration, existingImportIndex) => existingImportIndex >= minimumMergeTargetIndex && isImportDeclarationMergeSafe(existingImportDeclaration, importDeclaration, bindingCounts));
1625
+ if (mergeTargetIndex < 0) {
1626
+ mergedImportDeclarations.push(importDeclaration);
1627
+ } else {
1628
+ mergedImportDeclarations[mergeTargetIndex] = mergeImportDeclarations(mergedImportDeclarations[mergeTargetIndex], importDeclaration);
1629
+ }
1630
+ if (importDeclaration.isMergeBoundary) {
1631
+ minimumMergeTargetIndexByRequest.set(importRequestKey, mergedImportDeclarations.length);
1632
+ }
1633
+ }
1634
+ return mergedImportDeclarations;
1635
+ }
1636
+ function applyTypeImportStyle(importDeclaration, typeImportStyle) {
1637
+ if (importDeclaration.verbatimDeclaration !== null || !importDeclaration.namedSpecifiers) {
1638
+ return [importDeclaration];
1639
+ }
1640
+ const namedSpecifiers = importDeclaration.namedSpecifiers;
1641
+ if (typeImportStyle === "separate") {
1642
+ if (importDeclaration.isTypeOnly) {
1643
+ return [
1644
+ {
1645
+ ...importDeclaration,
1646
+ namedSpecifiers: sortNamedImportSpecifiers(namedSpecifiers)
1647
+ }
1648
+ ];
1649
+ }
1650
+ const typeSpecifiers2 = namedSpecifiers.filter((importSpecifier) => importSpecifier.isTypeOnly);
1651
+ const valueSpecifiers2 = namedSpecifiers.filter((importSpecifier) => !importSpecifier.isTypeOnly);
1652
+ if (typeSpecifiers2.length > 0 && (importDeclaration.importAttributes !== null || importDeclaration.leadingCommentsText.trim() !== "" || importDeclaration.trailingCommentsText !== "")) {
1653
+ return [
1654
+ {
1655
+ ...importDeclaration,
1656
+ namedSpecifiers: sortNamedImportSpecifiers(namedSpecifiers)
1657
+ }
1658
+ ];
1659
+ }
1660
+ const styledImportDeclarations = [];
1661
+ if (typeSpecifiers2.length > 0) {
1662
+ styledImportDeclarations.push({
1663
+ ...importDeclaration,
1664
+ isTypeOnly: true,
1665
+ defaultBinding: null,
1666
+ namespaceBinding: null,
1667
+ namedSpecifiers: sortNamedImportSpecifiers(typeSpecifiers2.map((importSpecifier) => ({
1668
+ ...importSpecifier,
1669
+ isTypeOnly: false
1670
+ })))
1671
+ });
1672
+ }
1673
+ if (valueSpecifiers2.length > 0 || importDeclaration.defaultBinding !== null || importDeclaration.namespaceBinding !== null) {
1674
+ let namedValueSpecifiers = null;
1675
+ if (valueSpecifiers2.length > 0) {
1676
+ namedValueSpecifiers = sortNamedImportSpecifiers(valueSpecifiers2);
715
1677
  }
1678
+ let leadingCommentsText = importDeclaration.leadingCommentsText;
1679
+ if (typeSpecifiers2.length > 0) {
1680
+ leadingCommentsText = "";
1681
+ }
1682
+ styledImportDeclarations.push({
1683
+ ...importDeclaration,
1684
+ namedSpecifiers: namedValueSpecifiers,
1685
+ leadingCommentsText
1686
+ });
716
1687
  }
1688
+ return styledImportDeclarations;
1689
+ }
1690
+ let inlineImportDeclaration = { ...importDeclaration, namedSpecifiers };
1691
+ if (importDeclaration.isTypeOnly) {
1692
+ inlineImportDeclaration = {
1693
+ ...importDeclaration,
1694
+ isTypeOnly: false,
1695
+ namedSpecifiers: namedSpecifiers.map((importSpecifier) => ({
1696
+ ...importSpecifier,
1697
+ isTypeOnly: true
1698
+ }))
1699
+ };
1700
+ }
1701
+ if (typeImportStyle === "mixed") {
1702
+ return [
1703
+ {
1704
+ ...inlineImportDeclaration,
1705
+ namedSpecifiers: sortNamedImportSpecifiers(inlineImportDeclaration.namedSpecifiers)
1706
+ }
1707
+ ];
1708
+ }
1709
+ const typeSpecifiers = sortNamedImportSpecifiers(inlineImportDeclaration.namedSpecifiers.filter((importSpecifier) => importSpecifier.isTypeOnly));
1710
+ const valueSpecifiers = sortNamedImportSpecifiers(inlineImportDeclaration.namedSpecifiers.filter((importSpecifier) => !importSpecifier.isTypeOnly));
1711
+ let orderedSpecifiers = [...valueSpecifiers, ...typeSpecifiers];
1712
+ if (typeImportStyle === "inline-first") {
1713
+ orderedSpecifiers = [...typeSpecifiers, ...valueSpecifiers];
1714
+ }
1715
+ return [{ ...inlineImportDeclaration, namedSpecifiers: orderedSpecifiers }];
1716
+ }
1717
+ function getImportBindingShapeRank(importDeclaration) {
1718
+ if (importDeclaration.defaultBinding !== null) {
1719
+ return 0;
1720
+ }
1721
+ if (importDeclaration.namespaceBinding !== null) {
1722
+ return 1;
1723
+ }
1724
+ return 2;
1725
+ }
1726
+ function sortImportSegment(importDeclarations, sortOptions, importGroupOrder) {
1727
+ let mergedImportDeclarations = [...importDeclarations];
1728
+ if (sortOptions.esmImportMerge) {
1729
+ mergedImportDeclarations = mergeCompatibleImports(importDeclarations);
1730
+ }
1731
+ const styledImportDeclarations = mergedImportDeclarations.flatMap((importDeclaration) => applyTypeImportStyle(importDeclaration, sortOptions.esmImportTypeStyle));
1732
+ const unlistedGroupRank = sortOptions.esmImportGroups.length;
1733
+ const rankedImportDeclarations = styledImportDeclarations.map((importDeclaration, originalIndex) => ({
1734
+ importDeclaration,
1735
+ importGroup: classifyImportGroup(importDeclaration.moduleSpecifier),
1736
+ originalIndex
1737
+ }));
1738
+ rankedImportDeclarations.sort((left, right) => {
1739
+ const groupRankDifference = (importGroupOrder.get(left.importGroup) ?? unlistedGroupRank) - (importGroupOrder.get(right.importGroup) ?? unlistedGroupRank);
1740
+ if (groupRankDifference !== 0) {
1741
+ return groupRankDifference;
1742
+ }
1743
+ const moduleSpecifierDifference = left.importDeclaration.moduleSpecifier.localeCompare(right.importDeclaration.moduleSpecifier, "en", { sensitivity: "base" });
1744
+ if (moduleSpecifierDifference !== 0) {
1745
+ return moduleSpecifierDifference;
1746
+ }
1747
+ if (left.importDeclaration.isTypeOnly !== right.importDeclaration.isTypeOnly) {
1748
+ if (left.importDeclaration.isTypeOnly) {
1749
+ return -1;
1750
+ }
1751
+ return 1;
1752
+ }
1753
+ return getImportBindingShapeRank(left.importDeclaration) - getImportBindingShapeRank(right.importDeclaration) || left.originalIndex - right.originalIndex;
1754
+ });
1755
+ let previousImportGroup = null;
1756
+ const renderedImportLines = [];
1757
+ for (const { importDeclaration, importGroup } of rankedImportDeclarations) {
1758
+ if (sortOptions.esmImportSeparation && previousImportGroup !== null && importGroup !== previousImportGroup) {
1759
+ renderedImportLines.push("");
1760
+ }
1761
+ renderedImportLines.push(renderImportDeclaration(importDeclaration));
1762
+ previousImportGroup = importGroup;
1763
+ }
1764
+ return renderedImportLines;
1765
+ }
1766
+ function renderSortedImportLines(importDeclarations, sortOptions) {
1767
+ let currentSortableImportDeclarations = [];
1768
+ const importGroupOrder = new Map(sortOptions.esmImportGroups.map((importGroup, groupIndex) => [
1769
+ importGroup,
1770
+ groupIndex
1771
+ ]));
1772
+ const importDeclarationChunks = [];
1773
+ for (const importDeclaration of importDeclarations) {
1774
+ if (!importDeclaration.isSideEffectOnly) {
1775
+ currentSortableImportDeclarations.push(importDeclaration);
1776
+ continue;
1777
+ }
1778
+ if (currentSortableImportDeclarations.length > 0) {
1779
+ importDeclarationChunks.push({
1780
+ kind: "sortable",
1781
+ importDeclarations: currentSortableImportDeclarations
1782
+ });
1783
+ currentSortableImportDeclarations = [];
1784
+ }
1785
+ importDeclarationChunks.push({ kind: "side-effect", importDeclaration });
1786
+ }
1787
+ if (currentSortableImportDeclarations.length > 0) {
1788
+ importDeclarationChunks.push({
1789
+ kind: "sortable",
1790
+ importDeclarations: currentSortableImportDeclarations
1791
+ });
1792
+ }
1793
+ let previousChunkKind = null;
1794
+ const renderedImportLines = [];
1795
+ for (const importChunk of importDeclarationChunks) {
1796
+ if (sortOptions.esmImportSeparation && previousChunkKind !== null && previousChunkKind !== importChunk.kind) {
1797
+ renderedImportLines.push("");
1798
+ }
1799
+ if (importChunk.kind === "sortable") {
1800
+ renderedImportLines.push(...sortImportSegment(importChunk.importDeclarations, sortOptions, importGroupOrder));
1801
+ } else {
1802
+ renderedImportLines.push(renderImportDeclaration(importChunk.importDeclaration));
1803
+ }
1804
+ previousChunkKind = importChunk.kind;
1805
+ }
1806
+ return renderedImportLines;
1807
+ }
1808
+ function isCommentPresentWithinRange(sortedComments, start, end) {
1809
+ const firstCommentIndex = findCommentIndexAtOrAfter(sortedComments, start);
1810
+ const firstComment = sortedComments[firstCommentIndex];
1811
+ return firstComment !== undefined && firstComment.textRange.end <= end;
1812
+ }
1813
+ function hasPositionSensitiveEslintCommentWithinRange(sourceText, sortedComments, start, end) {
1814
+ for (let commentIndex = findCommentIndexAtOrAfter(sortedComments, start);commentIndex < sortedComments.length; commentIndex++) {
1815
+ const parserComment = sortedComments[commentIndex];
1816
+ if (!parserComment || parserComment.textRange.start >= end) {
1817
+ return false;
1818
+ }
1819
+ if (parserComment.textRange.end <= end && isPositionSensitiveEslintComment(sourceText, parserComment)) {
1820
+ return true;
1821
+ }
1822
+ }
1823
+ return false;
1824
+ }
1825
+ function buildImportSegmentEdits(sourceText, importEntries, sortOptions, lineEnding, isBlankLineRequired) {
1826
+ const [firstImportEntry] = importEntries;
1827
+ if (!firstImportEntry) {
1828
+ return null;
1829
+ }
1830
+ let trailingLineBreaks = lineEnding;
1831
+ const replacementText = renderSortedImportLines(importEntries.map((importEntry) => importEntry.parsedImportDeclaration), sortOptions).join(lineEnding);
1832
+ const removalEdits = importEntries.slice(1).map((importEntry) => ({
1833
+ start: importEntry.start,
1834
+ end: importEntry.end,
1835
+ replacementText: ""
1836
+ }));
1837
+ if (isBlankLineRequired) {
1838
+ const textWithoutLaterImports = applySourceTextEdits(sourceText, removalEdits);
1839
+ if (textWithoutLaterImports === null) {
1840
+ return null;
1841
+ }
1842
+ const isFollowingContentPresent = textWithoutLaterImports.slice(firstImportEntry.end).trim() !== "";
1843
+ if (isFollowingContentPresent) {
1844
+ trailingLineBreaks += lineEnding;
1845
+ }
1846
+ }
1847
+ return [
1848
+ {
1849
+ start: firstImportEntry.start,
1850
+ end: firstImportEntry.end,
1851
+ replacementText: replacementText + trailingLineBreaks
1852
+ },
1853
+ ...removalEdits
1854
+ ];
1855
+ }
1856
+ function buildImportSortingEdits(sourceText, programStatements, sortedComments, sortOptions, isPrettierFilePragmaPresent) {
1857
+ const importDeclarationNodes = programStatements.filter((statement) => statement.type === "ImportDeclaration");
1858
+ if (importDeclarationNodes.length === 0) {
1859
+ return [];
1860
+ }
1861
+ const prettierFilePragmaComment = getPrettierFilePragmaComment(sortedComments, isPrettierFilePragmaPresent);
1862
+ if (isPrettierFilePragmaPresent && !prettierFilePragmaComment) {
1863
+ return [];
1864
+ }
1865
+ let currentSortableSegment = [];
1866
+ const sortableSegments = [];
1867
+ for (const importDeclarationNode of importDeclarationNodes) {
1868
+ const declarationRange = getAstNodeTextRange(importDeclarationNode);
1869
+ if (!declarationRange) {
1870
+ return [];
1871
+ }
1872
+ if (hasPositionSensitiveEslintCommentWithinRange(sourceText, sortedComments, declarationRange.start, declarationRange.end)) {
1873
+ return [];
1874
+ }
1875
+ const isImportIgnoredByPrettier = isPrettierIgnored(sourceText, declarationRange, sortedComments);
1876
+ const leadingCommentsStart = findLeadingCommentsStart(sourceText, declarationRange.start, sortedComments, prettierFilePragmaComment);
1877
+ const trailingComments = getTrailingComments(sourceText, declarationRange.end, sortedComments);
1878
+ if (isImportIgnoredByPrettier || trailingComments.isImportSortingBoundary) {
1879
+ if (currentSortableSegment.length > 0) {
1880
+ sortableSegments.push(currentSortableSegment);
1881
+ currentSortableSegment = [];
1882
+ }
1883
+ continue;
1884
+ }
1885
+ const parsedImportDeclaration = parseImportDeclaration({
1886
+ importDeclarationNode,
1887
+ declarationRange,
1888
+ leadingCommentsText: sourceText.slice(leadingCommentsStart, declarationRange.start),
1889
+ trailingCommentsText: trailingComments.commentsText,
1890
+ hasInternalComments: isCommentPresentWithinRange(sortedComments, declarationRange.start, declarationRange.end)
1891
+ }, sourceText);
1892
+ if (parsedImportDeclaration === null) {
1893
+ return [];
1894
+ }
1895
+ const parsedImportEntry = {
1896
+ importDeclarationNode,
1897
+ start: leadingCommentsStart,
1898
+ end: getRangeEndIncludingLineBreak(sourceText, trailingComments.rangeEnd),
1899
+ parsedImportDeclaration
1900
+ };
1901
+ const previousImportEntry = currentSortableSegment.at(-1);
1902
+ if (previousImportEntry && isCommentPresentWithinRange(sortedComments, previousImportEntry.end, parsedImportEntry.start)) {
1903
+ sortableSegments.push(currentSortableSegment);
1904
+ currentSortableSegment = [];
1905
+ }
1906
+ currentSortableSegment.push(parsedImportEntry);
1907
+ }
1908
+ if (currentSortableSegment.length > 0) {
1909
+ sortableSegments.push(currentSortableSegment);
1910
+ }
1911
+ let lineEnding = `
1912
+ `;
1913
+ if (sourceText.includes(`\r
1914
+ `)) {
1915
+ lineEnding = `\r
1916
+ `;
1917
+ }
1918
+ const sortingEdits = [];
1919
+ const lastImportDeclarationNode = importDeclarationNodes.at(-1);
1920
+ for (const sortableSegment of sortableSegments) {
1921
+ const isLastImportSegment = sortableSegment.at(-1)?.importDeclarationNode === lastImportDeclarationNode;
1922
+ const importSegmentEdits = buildImportSegmentEdits(sourceText, sortableSegment, sortOptions, lineEnding, isLastImportSegment);
1923
+ if (importSegmentEdits === null) {
1924
+ return [];
1925
+ }
1926
+ sortingEdits.push(...importSegmentEdits);
1927
+ }
1928
+ return sortingEdits;
1929
+ }
1930
+
1931
+ // src/sort-typescript.ts
1932
+ async function sortTypeScript(sourceText, prettierOptions, parser) {
1933
+ const sortOptions = resolveSortOptions(prettierOptions);
1934
+ if (!sortOptions.esmImportSort && !sortOptions.esmExportSpecifierSort) {
1935
+ return sourceText;
1936
+ }
1937
+ let parserAst;
1938
+ try {
1939
+ parserAst = await parser.parse(sourceText, prettierOptions);
1940
+ } catch {
1941
+ return sourceText;
1942
+ }
1943
+ const programStatements = getProgramStatements(parserAst);
1944
+ const sortedComments = getSortedAstCommentsWithTextRanges(getProgramComments(parserAst));
1945
+ const sortingEdits = [];
1946
+ if (sortOptions.esmImportSort) {
1947
+ const isPrettierFilePragmaPresent = parser.hasPragma?.(sourceText) ?? false;
1948
+ sortingEdits.push(...buildImportSortingEdits(sourceText, programStatements, sortedComments, sortOptions, isPrettierFilePragmaPresent));
1949
+ }
1950
+ if (sortOptions.esmExportSpecifierSort) {
1951
+ sortingEdits.push(...buildExportSortingEdits(sourceText, programStatements, sortedComments));
1952
+ }
1953
+ const sortedText = applySourceTextEdits(sourceText, sortingEdits);
1954
+ if (sortedText === null || sortedText === sourceText) {
1955
+ return sourceText;
1956
+ }
1957
+ try {
1958
+ await parser.parse(sortedText, prettierOptions);
1959
+ return sortedText;
1960
+ } catch {
1961
+ return sourceText;
717
1962
  }
718
- const indent = detectIndent(text);
719
- const output = JSON.stringify(result, null, indent);
720
- return text.endsWith(`
721
- `) ? output + `
722
- ` : output;
723
1963
  }
724
1964
 
725
1965
  // src/index.ts
726
- function wrap(parser, ...transforms) {
1966
+ function wrapParserPreprocess(parser, preprocessTransform) {
727
1967
  return {
728
1968
  ...parser,
729
- async preprocess(text, parserOptions) {
730
- let source = parser.preprocess ? await parser.preprocess(text, parserOptions) : text;
731
- for (const transform of transforms) {
732
- source = transform(source, parserOptions);
1969
+ async preprocess(sourceText, prettierOptions) {
1970
+ let transformedText = sourceText;
1971
+ if (parser.preprocess) {
1972
+ transformedText = await parser.preprocess(sourceText, prettierOptions);
733
1973
  }
734
- return source;
1974
+ return preprocessTransform(transformedText, prettierOptions, parser);
735
1975
  }
736
1976
  };
737
1977
  }
738
- var plugin = {
1978
+ var sortPlugin = {
739
1979
  options,
740
1980
  parsers: {
741
- babel: wrap(babelParsers.babel, sortImports, sortExports),
742
- "babel-ts": wrap(babelParsers["babel-ts"], sortImports, sortExports),
743
- typescript: wrap(typescriptParsers.typescript, sortImports, sortExports),
744
- "json-stringify": wrap(babelParsers["json-stringify"], sortPackageJson)
1981
+ acorn: wrapParserPreprocess(acornPlugin.parsers.acorn, sortTypeScript),
1982
+ babel: wrapParserPreprocess(babelPlugin.parsers.babel, sortTypeScript),
1983
+ "babel-flow": wrapParserPreprocess(babelPlugin.parsers["babel-flow"], sortTypeScript),
1984
+ "babel-ts": wrapParserPreprocess(babelPlugin.parsers["babel-ts"], sortTypeScript),
1985
+ espree: wrapParserPreprocess(acornPlugin.parsers.espree, sortTypeScript),
1986
+ flow: wrapParserPreprocess(flowPlugin.parsers.flow, sortTypeScript),
1987
+ json: wrapParserPreprocess(babelPlugin.parsers.json, preprocessPackageJson),
1988
+ "json-stringify": wrapParserPreprocess(babelPlugin.parsers["json-stringify"], preprocessPackageJson),
1989
+ meriyah: wrapParserPreprocess(meriyahPlugin.parsers.meriyah, sortTypeScript),
1990
+ typescript: wrapParserPreprocess(typescriptPlugin.parsers.typescript, sortTypeScript)
745
1991
  }
746
1992
  };
747
- var src_default = plugin;
1993
+ var src_default = sortPlugin;
748
1994
  export {
749
1995
  options,
750
1996
  src_default as default