prettier-plugin-sort 1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -30,7 +30,7 @@ import App from './App';
30
30
 
31
31
  ## Install
32
32
 
33
- Requires Prettier 3.9 or newer.
33
+ Requires Prettier 3.x, version 3.9 or later.
34
34
 
35
35
  ```shell
36
36
  npm i -D prettier prettier-plugin-sort
@@ -56,20 +56,20 @@ Dynamic `import()` calls and import-like text in strings or comments are left un
56
56
 
57
57
  `esmImportGroups` accepts these values:
58
58
 
59
- | Group | Matches |
60
- | ---------- | ------------------------------------------------------------------ |
61
- | `builtin` | Modules beginning with `node:` or `bun:`, plus `bun` |
62
- | `external` | Packages and module paths not matched by any other group |
63
- | `internal` | Path aliases defined by `compilerOptions.paths` in `tsconfig.json` |
64
- | `parent` | Parent-relative paths such as `../utils` |
65
- | `sibling` | Sibling paths such as `./Button` |
66
- | `index` | `.`, `./`, `./index`, and `./index` with an optional extension |
59
+ | Group | Matches |
60
+ | ---------- | ----------------------------------------------------------------------------------------------- |
61
+ | `builtin` | Modules beginning with `node:` or `bun:`, plus `bun` |
62
+ | `external` | Packages and module paths not matched by any other group |
63
+ | `internal` | Path aliases from `compilerOptions.paths` in the TypeScript project containing the current file |
64
+ | `parent` | Parent-relative paths such as `../utils` |
65
+ | `sibling` | Sibling paths such as `./Button` |
66
+ | `index` | `.`, `./`, `./index`, and `./index` with an optional extension |
67
67
 
68
68
  Modern Node.js code should reference built-ins explicitly with [`node:` URLs](https://nodejs.org/api/esm.html#node-imports). The plugin also classifies only `node:`-prefixed modules as `builtin`. Module specifiers without the prefix, such as `fs` and `path`, belong to `external`.
69
69
 
70
70
  The table also shows the default order. Duplicate entries are removed automatically, and omitted default groups are appended in their default order.
71
71
 
72
- The plugin searches upward from the current file for the nearest `tsconfig.json` and identifies `internal` imports from the resolved `compilerOptions.paths`. Inherited `paths` are supported. Non-relative paths not declared in `paths` remain `external`. The catch-all pattern `*` is ignored because it cannot distinguish project modules from third-party packages.
72
+ The plugin searches upward from the current file for the nearest `tsconfig.json`, then uses `files`, `include`, and `exclude` to select the TypeScript project containing the file, following `references` when necessary. Each project is resolved through `extends` before matching the file and reading `compilerOptions.paths`. Only `paths` from the selected project are used to classify `internal`; `paths` from other referenced projects are not merged. Non-relative paths not declared there remain `external`. The catch-all pattern `*` is ignored because it cannot distinguish project modules from third-party packages.
73
73
 
74
74
  With `esmImportSeparation` set to `false`, blank lines are removed both between groups and around side-effect imports.
75
75
 
@@ -94,6 +94,8 @@ With `esmImportMerge` enabled, imports from the same module are merged when safe
94
94
 
95
95
  Imports are not merged when their import attributes differ, comments cannot move safely, or default and namespace bindings conflict. Side-effect imports always remain separate.
96
96
 
97
+ An import with an attached comment keeps its position relative to surrounding declarations from the same module, preserving the merge boundary after sorting.
98
+
97
99
  ## Export sorting
98
100
 
99
101
  `esmExportSpecifierSort` sorts top-level `export { ... }` and `export type { ... }` entries by name. Aliased entries use their exported names.
@@ -262,6 +264,10 @@ The plugin only handles ES module syntax. It does not modify CommonJS `require()
262
264
 
263
265
  `tsconfig.json` sorting supports the `json` parser. The file must be named `tsconfig.json` or `tsconfig.*.json`.
264
266
 
267
+ Range formatting in JavaScript, TypeScript, Flow, or JSON files applies Prettier's layout rules and preserves the original order. For embedded code, the host parser determines the range. For example, Vue may expand a selection to the entire `<script>` block, which still participates in sorting.
268
+
269
+ With `formatWithCursor()`, the cursor follows the corresponding source content. If its position after sorting cannot be determined reliably, the plugin preserves the original order and continues with Prettier's formatting.
270
+
265
271
  ## TypeScript configuration
266
272
 
267
273
  The plugin also exports the `SortOptions`, `ImportGroup`, and `TypeImportStyle` types for use in TypeScript configuration files:
package/README.zh.md CHANGED
@@ -30,7 +30,7 @@ import App from './App';
30
30
 
31
31
  ## 安装
32
32
 
33
- 要求 Prettier 3.9 或更高版本。
33
+ 要求 Prettier 3.9 及以上的 3.x 版本。
34
34
 
35
35
  ```shell
36
36
  npm i -D prettier prettier-plugin-sort
@@ -56,20 +56,20 @@ npm i -D prettier prettier-plugin-sort
56
56
 
57
57
  `esmImportGroups` 支持以下分组:
58
58
 
59
- | 分组 | 匹配范围 |
60
- | ---------- | --------------------------------------------------------- |
61
- | `builtin` | 以 `node:`、`bun:` 开头的模块,以及 `bun` |
62
- | `external` | 第三方包,以及没有匹配其他分组的模块路径 |
63
- | `internal` | `tsconfig.json` `compilerOptions.paths` 定义的路径别名 |
64
- | `parent` | `../utils` 这类指向上级目录的相对路径 |
65
- | `sibling` | `./Button` 这类指向同级目录的相对路径 |
66
- | `index` | `.`、`./`、`./index` 及带扩展名的 `./index` |
59
+ | 分组 | 匹配范围 |
60
+ | ---------- | --------------------------------------------------------------------------- |
61
+ | `builtin` | 以 `node:`、`bun:` 开头的模块,以及 `bun` |
62
+ | `external` | 第三方包,以及没有匹配其他分组的模块路径 |
63
+ | `internal` | 当前文件所属 TypeScript project 的 `compilerOptions.paths` 中定义的路径别名 |
64
+ | `parent` | `../utils` 这类指向上级目录的相对路径 |
65
+ | `sibling` | `./Button` 这类指向同级目录的相对路径 |
66
+ | `index` | `.`、`./`、`./index` 及带扩展名的 `./index` |
67
67
 
68
68
  现代 Node.js 代码应使用 [`node:` URL](https://nodejs.org/api/esm.html#node-imports) 显式引用内置模块。本插件也只把带 `node:` 前缀的内置模块归入 `builtin`。`fs`、`path` 等未带前缀的模块归入 `external`。
69
69
 
70
70
  默认顺序就是表中的顺序。配置数组会自动去重,遗漏的默认分组则按默认顺序补到末尾。
71
71
 
72
- 插件会从当前文件所在目录向上查找最近的 `tsconfig.json`,并根据解析后的 `compilerOptions.paths` 识别 `internal`。继承配置中的 `paths` 同样有效。没有在 `paths` 中声明的非相对路径仍归入 `external`。`*` 无法区分项目代码和第三方包,因此不会用于判断 `internal`。
72
+ 插件会从当前文件所在目录向上查找最近的 `tsconfig.json`,根据 `files`、`include` 和 `exclude` 选择包含当前文件的 TypeScript project,并在需要时沿 `references` 查找。每个 project 都会先解析 `extends`,再匹配文件并读取 `compilerOptions.paths`。识别 `internal` 时只使用当前文件所属 project 的 `paths`,不会合并其他 referenced project 的 `paths`。没有在该 `paths` 中声明的非相对路径仍归入 `external`。`*` 无法区分项目代码和第三方包,因此不会用于判断 `internal`。
73
73
 
74
74
  将 `esmImportSeparation` 设为 `false` 后,分组之间和副作用 `import` 上下两侧都不再留空行。
75
75
 
@@ -94,6 +94,8 @@ npm i -D prettier prettier-plugin-sort
94
94
 
95
95
  `import` 属性不同、注释无法安全移动,或默认导入与命名空间导入发生冲突时,都不会合并。副作用 `import` 始终保持独立。
96
96
 
97
+ 带注释的 `import` 会保留与两侧同源声明的相对顺序,避免排序后改变合并边界。
98
+
97
99
  ## `export` 排序
98
100
 
99
101
  `esmExportSpecifierSort` 按名称排列顶层 `export { ... }` 和 `export type { ... }`。使用 `as` 时,以导出后的名称为准。
@@ -262,6 +264,10 @@ Vue、Markdown 等文件中的 JavaScript 和 TypeScript 的**嵌入式代码**
262
264
 
263
265
  `tsconfig.json` 排序支持 `json` 解析器,文件名必须是 `tsconfig.json` 或 `tsconfig.*.json`。
264
266
 
267
+ 直接格式化 JavaScript、TypeScript、Flow 或 JSON 文件的局部选区时,插件只执行 Prettier 排版,保留内容原有顺序。嵌入式代码的范围由宿主解析器决定。例如 Vue 可能将选区扩展为整个 `<script>` 块,该代码块仍会参与排序。
268
+
269
+ 使用 `formatWithCursor()` 时,光标会跟随对应的源码内容。无法明确定位排序后的光标位置时,插件保留原有顺序,继续执行 Prettier 排版。
270
+
265
271
  ## TypeScript 配置
266
272
 
267
273
  插件还导出 `SortOptions`、`ImportGroup` 和 `TypeImportStyle` 类型,可直接用于 TypeScript 配置文件:
@@ -0,0 +1,4 @@
1
+ import { type ParserOptions } from 'prettier';
2
+ import { type ParserAstNode } from '#/parser-ast';
3
+ /** 在 Prettier 根据新 AST 定位光标之前,先跟随源码未变的唯一节点移动。 */
4
+ export declare function updateCursorOffset(sourceText: string, sortedText: string, originalAst: ParserAstNode, sortedAst: ParserAstNode, prettierOptions: ParserOptions): boolean;
package/dist/index.js CHANGED
@@ -1,11 +1,3 @@
1
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
- }) : x)(function(x) {
4
- if (typeof require !== "undefined")
5
- return require.apply(this, arguments);
6
- throw Error('Dynamic require of "' + x + '" is not supported');
7
- });
8
-
9
1
  // src/index.ts
10
2
  import acornPlugin from "prettier/plugins/acorn";
11
3
  import babelPlugin from "prettier/plugins/babel";
@@ -32,14 +24,7 @@ var DEFAULT_SORT_OPTIONS = {
32
24
  tsconfigSort: true,
33
25
  tsconfigSeparation: true
34
26
  };
35
- var VALID_IMPORT_GROUPS = new Set([
36
- "builtin",
37
- "external",
38
- "internal",
39
- "parent",
40
- "sibling",
41
- "index"
42
- ]);
27
+ var VALID_IMPORT_GROUPS = new Set(DEFAULT_SORT_OPTIONS.esmImportGroups);
43
28
  var VALID_TYPE_IMPORT_STYLES = new Set([
44
29
  "separate",
45
30
  "inline-first",
@@ -58,7 +43,10 @@ function resolveBooleanSortOption(prettierOptions, optionName) {
58
43
  function resolveEsmOptions(prettierOptions) {
59
44
  const configuredImportGroups = Array.isArray(prettierOptions.esmImportGroups) ? [...new Set(prettierOptions.esmImportGroups.filter(isValidImportGroup))] : [];
60
45
  const remainingDefaultImportGroups = DEFAULT_SORT_OPTIONS.esmImportGroups.filter((importGroup) => !configuredImportGroups.includes(importGroup));
61
- const resolvedImportGroups = configuredImportGroups.length > 0 ? [...configuredImportGroups, ...remainingDefaultImportGroups] : [...DEFAULT_SORT_OPTIONS.esmImportGroups];
46
+ const resolvedImportGroups = [
47
+ ...configuredImportGroups,
48
+ ...remainingDefaultImportGroups
49
+ ];
62
50
  const resolvedTypeImportStyle = isValidTypeImportStyle(prettierOptions.esmImportTypeStyle) ? prettierOptions.esmImportTypeStyle : DEFAULT_SORT_OPTIONS.esmImportTypeStyle;
63
51
  return {
64
52
  esmImportSort: resolveBooleanSortOption(prettierOptions, "esmImportSort"),
@@ -306,6 +294,123 @@ function isPrettierIgnored(sourceText, statementRange, sortedComments) {
306
294
  return false;
307
295
  }
308
296
 
297
+ // src/cursor.ts
298
+ function collectCursorAnchors(parserAst, sourceText) {
299
+ const anchors = [];
300
+ function addAnchor(path, node) {
301
+ const range = getAstNodeTextRange(node);
302
+ if (!range || range.end > sourceText.length) {
303
+ return;
304
+ }
305
+ anchors.push({
306
+ ...range,
307
+ key: JSON.stringify([...path, node?.type ?? "comment"])
308
+ });
309
+ }
310
+ function collectJsonAnchors(node, path) {
311
+ addAnchor(path, node);
312
+ if (node.type === "ObjectExpression") {
313
+ for (const property of node.properties ?? []) {
314
+ const name = getAstNodeName(property.key);
315
+ if (name === null || !isParserAstNode(property.value)) {
316
+ continue;
317
+ }
318
+ const propertyPath = [...path, "property", name];
319
+ addAnchor(propertyPath, property);
320
+ addAnchor([...propertyPath, "key"], property.key);
321
+ collectJsonAnchors(property.value, [...propertyPath, "value"]);
322
+ }
323
+ } else if (node.type === "ArrayExpression") {
324
+ for (const [index, element] of (node.elements ?? []).entries()) {
325
+ if (!element) {
326
+ continue;
327
+ }
328
+ const elementPath = typeof element.value === "string" ? [...path, "string", element.value] : [...path, "index", index];
329
+ collectJsonAnchors(element, elementPath);
330
+ }
331
+ }
332
+ }
333
+ const jsonRoot = parserAst.type === "JsonRoot" ? parserAst.node : undefined;
334
+ if (jsonRoot) {
335
+ collectJsonAnchors(jsonRoot, ["json"]);
336
+ } else {
337
+ let statementIndex = 0;
338
+ for (const statement of getProgramStatements(parserAst)) {
339
+ if (statement.type === "ImportDeclaration") {
340
+ const sourceName = getAstNodeName(statement.source);
341
+ if (sourceName === null) {
342
+ continue;
343
+ }
344
+ const importPath = [
345
+ "import",
346
+ sourceName,
347
+ typeof statement.phase === "string" ? statement.phase : ""
348
+ ];
349
+ addAnchor([...importPath, "declaration"], statement);
350
+ addAnchor([...importPath, "source"], statement.source);
351
+ for (const specifier of statement.specifiers ?? []) {
352
+ const localName = getAstNodeName(specifier.local);
353
+ if (localName === null) {
354
+ continue;
355
+ }
356
+ const specifierPath = [...importPath, "binding", localName];
357
+ addAnchor(specifierPath, specifier);
358
+ addAnchor([...specifierPath, "local"], specifier.local);
359
+ addAnchor([...specifierPath, "imported"], specifier.imported);
360
+ }
361
+ continue;
362
+ }
363
+ const statementPath = ["statement", statementIndex++];
364
+ addAnchor(statementPath, statement);
365
+ if (statement.type === "ExportNamedDeclaration") {
366
+ addAnchor([...statementPath, "source"], statement.source);
367
+ for (const specifier of statement.specifiers ?? []) {
368
+ const exportedName = getAstNodeName(specifier.exported);
369
+ if (exportedName === null) {
370
+ continue;
371
+ }
372
+ const specifierPath = [...statementPath, "specifier", exportedName];
373
+ addAnchor(specifierPath, specifier);
374
+ addAnchor([...specifierPath, "local"], specifier.local);
375
+ addAnchor([...specifierPath, "exported"], specifier.exported);
376
+ }
377
+ }
378
+ }
379
+ }
380
+ for (const comment of getProgramComments(parserAst)) {
381
+ const range = getAstNodeTextRange(comment);
382
+ if (range) {
383
+ addAnchor(["comment", sourceText.slice(range.start, range.end)], comment);
384
+ }
385
+ }
386
+ return anchors;
387
+ }
388
+ function updateCursorOffset(sourceText, sortedText, originalAst, sortedAst, prettierOptions) {
389
+ const cursorOffset = prettierOptions.cursorOffset;
390
+ if (typeof cursorOffset !== "number" || cursorOffset < 0 || sourceText === sortedText) {
391
+ return true;
392
+ }
393
+ if (!Number.isSafeInteger(cursorOffset) || cursorOffset > sourceText.length) {
394
+ return false;
395
+ }
396
+ if (cursorOffset === 0 || cursorOffset === sourceText.length) {
397
+ prettierOptions.cursorOffset = cursorOffset === 0 ? 0 : sortedText.length;
398
+ return true;
399
+ }
400
+ const originalAnchors = collectCursorAnchors(originalAst, sourceText).filter((anchor) => anchor.start <= cursorOffset && cursorOffset <= anchor.end).sort((left, right) => left.end - left.start - (right.end - right.start) || right.start - left.start);
401
+ const sortedAnchors = collectCursorAnchors(sortedAst, sortedText);
402
+ for (const originalAnchor of originalAnchors) {
403
+ const originalNodeText = sourceText.slice(originalAnchor.start, originalAnchor.end);
404
+ const matches = sortedAnchors.filter((anchor) => anchor.key === originalAnchor.key && sortedText.slice(anchor.start, anchor.end) === originalNodeText);
405
+ const [matchingAnchor] = matches;
406
+ if (matches.length === 1 && matchingAnchor) {
407
+ prettierOptions.cursorOffset = matchingAnchor.start + cursorOffset - originalAnchor.start;
408
+ return true;
409
+ }
410
+ }
411
+ return false;
412
+ }
413
+
309
414
  // src/utils/package-rules.ts
310
415
  var PACKAGE_JSON_FIELD_ORDER = [
311
416
  "$schema",
@@ -518,6 +623,7 @@ class JsonSourceLiteral {
518
623
  this.sourceText = sourceText;
519
624
  }
520
625
  }
626
+ var JSON_KEY_ORDER = Symbol("jsonKeyOrder");
521
627
  var SEQUENTIAL_SCRIPT_PATTERN = /(?<=^|[\s&;<>|(])(?:run-s|npm-run-all2? .*(?:--sequential|--serial|-s))(?=$|[\s&;<>|)])/;
522
628
  var PACKAGE_JSON_FIELD_INDEXES = new Map(PACKAGE_JSON_FIELD_ORDER.map((fieldName, index) => [fieldName, index]));
523
629
  function compareStringsCaseSensitive(leftText, rightText) {
@@ -529,6 +635,19 @@ function compareStringsCaseSensitive(leftText, rightText) {
529
635
  function isJsonObject(value) {
530
636
  return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof JsonSourceLiteral);
531
637
  }
638
+ function createJsonObject(entries) {
639
+ const fields = new Map(entries);
640
+ return {
641
+ ...Object.fromEntries(fields),
642
+ [JSON_KEY_ORDER]: [...fields.keys()]
643
+ };
644
+ }
645
+ function getJsonKeys(jsonObject) {
646
+ return jsonObject[JSON_KEY_ORDER] ?? Object.keys(jsonObject);
647
+ }
648
+ function getJsonEntries(jsonObject) {
649
+ return getJsonKeys(jsonObject).map((key) => [key, jsonObject[key]]);
650
+ }
532
651
  function getJsonPathKey(jsonPath) {
533
652
  return JSON.stringify(jsonPath);
534
653
  }
@@ -585,6 +704,7 @@ function preserveJsonSourceLiterals(jsonValue, parserJsonNode, sourceText, field
585
704
  fieldNameSourceTexts.set(getJsonPathKey(propertyPath), sourceText.slice(propertyNameRange.start, propertyNameRange.end));
586
705
  jsonObject[propertyName] = propertyValue;
587
706
  }
707
+ jsonObject[JSON_KEY_ORDER] = [...propertyNames];
588
708
  return jsonObject;
589
709
  }
590
710
  if (parserJsonNode.type === "ArrayExpression") {
@@ -629,7 +749,7 @@ function serializeJsonValue(jsonValue, fieldNameSourceTexts, jsonPath = []) {
629
749
  }
630
750
  if (isJsonObject(jsonValue)) {
631
751
  const serializedFields = [];
632
- for (const [fieldName, fieldValue] of Object.entries(jsonValue)) {
752
+ for (const [fieldName, fieldValue] of getJsonEntries(jsonValue)) {
633
753
  const fieldPath = [...jsonPath, fieldName];
634
754
  const fieldNameSourceText = fieldNameSourceTexts.get(getJsonPathKey(fieldPath));
635
755
  const serializedValue = serializeJsonValue(fieldValue, fieldNameSourceTexts, fieldPath);
@@ -656,7 +776,7 @@ function deduplicateStringValues(stringValues) {
656
776
  return [...uniqueStringValues.values()];
657
777
  }
658
778
  function sortJsonObject(jsonObject, compareKeys = compareStringsCaseSensitive) {
659
- return Object.fromEntries(Object.entries(jsonObject).sort(([leftKey], [rightKey]) => compareKeys(leftKey, rightKey)));
779
+ return createJsonObject(getJsonEntries(jsonObject).sort(([leftKey], [rightKey]) => compareKeys(leftKey, rightKey)));
660
780
  }
661
781
  function sortJsonObjectByKeyOrder(jsonObject, keyOrder) {
662
782
  const keyOrderIndexes = new Map(keyOrder.map((key, index) => [key, index]));
@@ -678,7 +798,7 @@ function sortJsonObjectByKeyOrder(jsonObject, keyOrder) {
678
798
  });
679
799
  }
680
800
  function sortJsonObjectRecursively(jsonObject, sortObject = sortJsonObject) {
681
- const sortedNestedValues = Object.fromEntries(Object.entries(jsonObject).map(([key, value]) => [
801
+ const sortedNestedValues = createJsonObject(getJsonEntries(jsonObject).map(([key, value]) => [
682
802
  key,
683
803
  isJsonObject(value) ? sortJsonObjectRecursively(value, sortObject) : value
684
804
  ]));
@@ -786,11 +906,7 @@ function sortEslintConfigValue(fieldValue) {
786
906
  return sortJsonObjectValue(fieldValue, sortEslintConfigObject);
787
907
  }
788
908
  function sortPrettierConfigObject(prettierConfig) {
789
- const keyOrder = Object.keys(prettierConfig).filter((key) => key !== "overrides").sort(compareStringsCaseSensitive);
790
- if (Object.hasOwn(prettierConfig, "overrides")) {
791
- keyOrder.push("overrides");
792
- }
793
- const sortedConfig = sortJsonObjectByKeyOrder(prettierConfig, keyOrder);
909
+ const sortedConfig = sortJsonObject(prettierConfig, (leftKey, rightKey) => Number(leftKey === "overrides") - Number(rightKey === "overrides") || compareStringsCaseSensitive(leftKey, rightKey));
794
910
  if (Array.isArray(sortedConfig.overrides)) {
795
911
  sortedConfig.overrides = sortedConfig.overrides.map((override) => {
796
912
  if (!isJsonObject(override)) {
@@ -814,7 +930,7 @@ function sortWireitScriptObject(scriptConfig) {
814
930
  sortedConfig.dependencies = sortedConfig.dependencies.map((dependency) => sortJsonObjectValueByKeyOrder(dependency, ["script", "cascade"]));
815
931
  }
816
932
  if (isJsonObject(sortedConfig.env)) {
817
- sortedConfig.env = sortJsonObject(Object.fromEntries(Object.entries(sortedConfig.env).map(([name, value]) => [
933
+ sortedConfig.env = sortJsonObject(createJsonObject(getJsonEntries(sortedConfig.env).map(([name, value]) => [
818
934
  name,
819
935
  sortJsonObjectValueByKeyOrder(value, ["external", "default"])
820
936
  ])));
@@ -833,7 +949,7 @@ function sortWireitValue(fieldValue) {
833
949
  if (!isJsonObject(fieldValue)) {
834
950
  return fieldValue;
835
951
  }
836
- return sortJsonObject(Object.fromEntries(Object.entries(fieldValue).map(([scriptName, scriptConfig]) => [
952
+ return sortJsonObject(createJsonObject(getJsonEntries(fieldValue).map(([scriptName, scriptConfig]) => [
837
953
  scriptName,
838
954
  sortJsonObjectValue(scriptConfig, sortWireitScriptObject)
839
955
  ])));
@@ -883,7 +999,7 @@ function sortScriptsValue(fieldValue, packageJson) {
883
999
  if (!isJsonObject(fieldValue)) {
884
1000
  return fieldValue;
885
1001
  }
886
- const scriptNames = Object.keys(fieldValue);
1002
+ const scriptNames = getJsonKeys(fieldValue);
887
1003
  const baseScriptNames = new Set;
888
1004
  const normalizedScriptNames = scriptNames.map((scriptName) => {
889
1005
  const baseScriptName = scriptName.replace(/^(?:pre|post)/, "");
@@ -895,21 +1011,22 @@ function sortScriptsValue(fieldValue, packageJson) {
895
1011
  }
896
1012
  return scriptName;
897
1013
  });
898
- const orderedBaseScriptNames = hasSequentialScript(packageJson) ? [...new Set(normalizedScriptNames)] : sortScriptNames(normalizedScriptNames);
1014
+ const uniqueScriptNames = [...new Set(normalizedScriptNames)];
1015
+ const orderedBaseScriptNames = hasSequentialScript(packageJson) ? uniqueScriptNames : sortScriptNames(uniqueScriptNames);
899
1016
  const orderedScriptNames = orderedBaseScriptNames.flatMap((baseScriptName) => baseScriptNames.has(baseScriptName) ? [`pre${baseScriptName}`, baseScriptName, `post${baseScriptName}`] : [baseScriptName]);
900
- return Object.fromEntries(orderedScriptNames.filter((scriptName) => Object.hasOwn(fieldValue, scriptName)).map((scriptName) => [scriptName, fieldValue[scriptName]]));
1017
+ return createJsonObject(orderedScriptNames.filter((scriptName) => Object.hasOwn(fieldValue, scriptName)).map((scriptName) => [scriptName, fieldValue[scriptName]]));
901
1018
  }
902
1019
  function sortExportsValue(fieldValue) {
903
1020
  if (!isJsonObject(fieldValue)) {
904
1021
  return fieldValue;
905
1022
  }
906
- const exportKeys = Object.keys(fieldValue);
1023
+ const exportKeys = getJsonKeys(fieldValue);
907
1024
  const exportPathKeys = exportKeys.filter((key) => key.startsWith("."));
908
1025
  const exportConditionKeys = exportKeys.filter((key) => !key.startsWith(".") && key !== "default");
909
1026
  if (Object.hasOwn(fieldValue, "default")) {
910
1027
  exportConditionKeys.push("default");
911
1028
  }
912
- return Object.fromEntries([...exportPathKeys, ...exportConditionKeys].map((exportKey) => [exportKey, sortExportsValue(fieldValue[exportKey])]));
1029
+ return createJsonObject([...exportPathKeys, ...exportConditionKeys].map((exportKey) => [exportKey, sortExportsValue(fieldValue[exportKey])]));
913
1030
  }
914
1031
  function sortDevEnginesValue(fieldValue) {
915
1032
  if (!isJsonObject(fieldValue) || !isJsonObject(fieldValue.packageManager)) {
@@ -1107,7 +1224,7 @@ var PACKAGE_JSON_FIELD_SORTERS = {
1107
1224
  };
1108
1225
  function sortPackageJsonObject(packageJson) {
1109
1226
  const sortedPackageJsonFields = sortPackageJsonFields(packageJson);
1110
- return Object.fromEntries(Object.entries(sortedPackageJsonFields).map(([fieldName, fieldValue]) => {
1227
+ return createJsonObject(getJsonEntries(sortedPackageJsonFields).map(([fieldName, fieldValue]) => {
1111
1228
  const fieldSorter = Object.hasOwn(PACKAGE_JSON_FIELD_SORTERS, fieldName) ? PACKAGE_JSON_FIELD_SORTERS[fieldName] : undefined;
1112
1229
  return [
1113
1230
  fieldName,
@@ -1136,7 +1253,17 @@ async function preprocessPackageJson(sourceText, prettierOptions, parser) {
1136
1253
  if (!isJsonObject(packageJsonWithSourceLiterals)) {
1137
1254
  return sourceText;
1138
1255
  }
1139
- return serializeJsonValue(sortPackageJsonObject(packageJsonWithSourceLiterals), fieldNameSourceTexts) ?? sourceText;
1256
+ const sortedText = serializeJsonValue(sortPackageJsonObject(packageJsonWithSourceLiterals), fieldNameSourceTexts);
1257
+ if (sortedText === null || sortedText === sourceText) {
1258
+ return sourceText;
1259
+ }
1260
+ if (typeof prettierOptions.cursorOffset === "number" && prettierOptions.cursorOffset >= 0) {
1261
+ const sortedAst = await parser.parse(sortedText, prettierOptions);
1262
+ if (!isParserAstNode(sortedAst) || !updateCursorOffset(sourceText, sortedText, parserJsonAst, sortedAst, prettierOptions)) {
1263
+ return sourceText;
1264
+ }
1265
+ }
1266
+ return sortedText;
1140
1267
  } catch {
1141
1268
  return sourceText;
1142
1269
  }
@@ -1463,7 +1590,7 @@ async function preprocessTsconfig(sourceText, prettierOptions, parser) {
1463
1590
  if (compilerOptions) {
1464
1591
  const propertyRange = getAstNodeTextRange(compilerOptions.property);
1465
1592
  const valueRange = getAstNodeTextRange(compilerOptions.value);
1466
- if (propertyRange && valueRange && !isPrettierIgnored(sourceText, propertyRange, sortedComments)) {
1593
+ if (propertyRange && valueRange && !isPrettierIgnored(sourceText, propertyRange, sortedComments) && !isPrettierIgnored(sourceText, valueRange, sortedComments)) {
1467
1594
  const renderedCompilerOptions = renderObject(sourceText, compilerOptions.value, sortedComments, COMPILER_OPTION_FIELD_INDEXES, tsconfigSeparation);
1468
1595
  const renderedProperty = renderedCompilerOptions ? replacePropertyValue(sourceText, compilerOptions.property, compilerOptions.value, renderedCompilerOptions.sourceText) : null;
1469
1596
  if (renderedCompilerOptions && renderedProperty) {
@@ -1496,7 +1623,10 @@ async function preprocessTsconfig(sourceText, prettierOptions, parser) {
1496
1623
  if (editedSourceText === null) {
1497
1624
  return sourceText;
1498
1625
  }
1499
- await parser.parse(editedSourceText, prettierOptions);
1626
+ const sortedAst = await parser.parse(editedSourceText, prettierOptions);
1627
+ if (!updateCursorOffset(sourceText, editedSourceText, parserAst, sortedAst, prettierOptions)) {
1628
+ return sourceText;
1629
+ }
1500
1630
  return editedSourceText;
1501
1631
  } catch {
1502
1632
  return sourceText;
@@ -1692,7 +1822,11 @@ function findLeadingCommentsStart(sourceText, statementStart, sortedComments, pr
1692
1822
  }
1693
1823
  const textBetweenCommentAndImport = sourceText.slice(textRange.end, attachedTextStart);
1694
1824
  const lineBreakCount = textBetweenCommentAndImport.match(/\n/g)?.length ?? 0;
1695
- const isEslintNextLineCommentAttached = attachedTextStart === statementStart && isEslintNextLineComment(comment) && textBetweenCommentAndImport.trim() === "" && lineBreakCount === 1;
1825
+ const isEslintNextLine = isEslintNextLineComment(comment);
1826
+ if (isEslintNextLine && lineBreakCount === 0) {
1827
+ return null;
1828
+ }
1829
+ const isEslintNextLineCommentAttached = attachedTextStart === statementStart && isEslintNextLine && textBetweenCommentAndImport.trim() === "" && lineBreakCount === 1;
1696
1830
  if (isEslintNextLineCommentAttached) {
1697
1831
  attachedTextStart = textRange.start;
1698
1832
  continue;
@@ -1761,19 +1895,47 @@ function getTrailingComments(sourceText, statementEnd, sortedComments) {
1761
1895
  isImportSortingBoundary
1762
1896
  };
1763
1897
  }
1898
+ function getImportAttributesKey(importDeclarationNode, syntax) {
1899
+ const attributeNodes = importDeclarationNode.attributes;
1900
+ if (!Array.isArray(attributeNodes)) {
1901
+ return null;
1902
+ }
1903
+ const attributes = [];
1904
+ const attributeNames = new Set;
1905
+ for (const attributeNode of attributeNodes) {
1906
+ if (!isParserAstNode(attributeNode) || attributeNode.type !== "ImportAttribute" || !isParserAstNode(attributeNode.value)) {
1907
+ return null;
1908
+ }
1909
+ const keyNode = attributeNode.key;
1910
+ const valueNode = attributeNode.value;
1911
+ if (!keyNode || !["Identifier", "Literal", "StringLiteral"].includes(keyNode.type) || valueNode.type !== "Literal" && valueNode.type !== "StringLiteral" || typeof valueNode.value !== "string") {
1912
+ return null;
1913
+ }
1914
+ const attributeName = getAstNodeName(keyNode);
1915
+ if (attributeName === null || attributeNames.has(attributeName)) {
1916
+ return null;
1917
+ }
1918
+ attributeNames.add(attributeName);
1919
+ attributes.push([attributeName, valueNode.value]);
1920
+ }
1921
+ return JSON.stringify([syntax, attributes]);
1922
+ }
1764
1923
  function parseImportAttributes(sourceText, importDeclarationNode) {
1765
1924
  const declarationRange = getAstNodeTextRange(importDeclarationNode);
1766
1925
  const moduleSpecifierRange = getAstNodeTextRange(importDeclarationNode.source);
1767
1926
  if (!declarationRange || !moduleSpecifierRange) {
1768
- return { attributesText: null, isValid: false };
1927
+ return { attributesText: null, attributesKey: null, isValid: false };
1769
1928
  }
1770
1929
  const importAttributesText = sourceText.slice(moduleSpecifierRange.end, declarationRange.end).replace(/;\s*$/, "").trim();
1771
1930
  if (importAttributesText === "") {
1772
- return { attributesText: null, isValid: true };
1931
+ return { attributesText: null, attributesKey: null, isValid: true };
1773
1932
  }
1933
+ const attributeSyntax = /^(with|assert)\s*\{[\s\S]*\}$/.exec(importAttributesText)?.[1];
1934
+ const attributesKey = attributeSyntax ? getImportAttributesKey(importDeclarationNode, attributeSyntax) : null;
1774
1935
  return {
1775
1936
  attributesText: importAttributesText,
1776
- isValid: /^(?:with|assert)\s*\{[\s\S]*\}$/.test(importAttributesText)
1937
+ attributesKey,
1938
+ isValid: attributesKey !== null
1777
1939
  };
1778
1940
  }
1779
1941
  function parseImportDeclaration(importDeclarationMetadata, sourceText) {
@@ -1863,6 +2025,7 @@ function parseImportDeclaration(importDeclarationMetadata, sourceText) {
1863
2025
  namespaceBinding,
1864
2026
  namedSpecifiers: parsedNamedSpecifiers,
1865
2027
  importAttributes: parsedImportAttributes.attributesText,
2028
+ importAttributesKey: parsedImportAttributes.attributesKey,
1866
2029
  verbatimDeclaration,
1867
2030
  leadingCommentsText,
1868
2031
  trailingCommentsText,
@@ -1928,10 +2091,10 @@ function getLocalBindingNames(importDeclaration) {
1928
2091
  return localBindingNames;
1929
2092
  }
1930
2093
  function getImportRequestKey(importDeclaration) {
1931
- return `${importDeclaration.moduleSpecifier}\x00${importDeclaration.importAttributes ?? ""}`;
2094
+ return `${importDeclaration.moduleSpecifier}\x00${importDeclaration.importAttributesKey ?? importDeclaration.importAttributes ?? ""}`;
1932
2095
  }
1933
2096
  function isImportDeclarationMergeSafe(targetImportDeclaration, candidateImportDeclaration, bindingCounts) {
1934
- if (targetImportDeclaration.verbatimDeclaration !== null || candidateImportDeclaration.verbatimDeclaration !== null || targetImportDeclaration.isSideEffectOnly || candidateImportDeclaration.isSideEffectOnly || targetImportDeclaration.moduleSpecifier !== candidateImportDeclaration.moduleSpecifier || targetImportDeclaration.importAttributes !== candidateImportDeclaration.importAttributes || targetImportDeclaration.isMergeBoundary || candidateImportDeclaration.isMergeBoundary) {
2097
+ if (targetImportDeclaration.verbatimDeclaration !== null || candidateImportDeclaration.verbatimDeclaration !== null || targetImportDeclaration.moduleSpecifier !== candidateImportDeclaration.moduleSpecifier || targetImportDeclaration.importAttributesKey !== candidateImportDeclaration.importAttributesKey || targetImportDeclaration.isMergeBoundary || candidateImportDeclaration.isMergeBoundary) {
1935
2098
  return false;
1936
2099
  }
1937
2100
  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) {
@@ -1963,6 +2126,7 @@ function mergeImportDeclarations(targetImportDeclaration, candidateImportDeclara
1963
2126
  namespaceBinding: targetImportDeclaration.namespaceBinding ?? candidateImportDeclaration.namespaceBinding,
1964
2127
  namedSpecifiers,
1965
2128
  importAttributes: targetImportDeclaration.importAttributes,
2129
+ importAttributesKey: targetImportDeclaration.importAttributesKey,
1966
2130
  verbatimDeclaration: null,
1967
2131
  leadingCommentsText: targetImportDeclaration.leadingCommentsText,
1968
2132
  trailingCommentsText: "",
@@ -1985,7 +2149,7 @@ function mergeCompatibleImports(importDeclarations) {
1985
2149
  currentBindingCountsByRequest.delete(importRequestKey);
1986
2150
  continue;
1987
2151
  }
1988
- if (importDeclaration.verbatimDeclaration !== null || importDeclaration.isSideEffectOnly) {
2152
+ if (importDeclaration.verbatimDeclaration !== null) {
1989
2153
  continue;
1990
2154
  }
1991
2155
  const bindingCounts = currentBindingCountsByRequest.get(importRequestKey) ?? {
@@ -2035,10 +2199,10 @@ function applyTypeImportStyle(importDeclaration, typeImportStyle) {
2035
2199
  ];
2036
2200
  }
2037
2201
  if (typeImportStyle === "separate") {
2038
- const typeSpecifiers2 = namedSpecifiers.filter((importSpecifier) => importSpecifier.isTypeOnly);
2039
- const valueSpecifiers2 = namedSpecifiers.filter((importSpecifier) => !importSpecifier.isTypeOnly);
2040
- const hasValueBinding = valueSpecifiers2.length > 0 || importDeclaration.defaultBinding !== null || importDeclaration.namespaceBinding !== null;
2041
- if (typeSpecifiers2.length > 0 && (!hasValueBinding || importDeclaration.importAttributes !== null || importDeclaration.leadingCommentsText.trim() !== "" || importDeclaration.trailingCommentsText !== "")) {
2202
+ const typeSpecifiers = namedSpecifiers.filter((importSpecifier) => importSpecifier.isTypeOnly);
2203
+ const valueSpecifiers = namedSpecifiers.filter((importSpecifier) => !importSpecifier.isTypeOnly);
2204
+ const hasValueBinding = valueSpecifiers.length > 0 || importDeclaration.defaultBinding !== null || importDeclaration.namespaceBinding !== null;
2205
+ if (typeSpecifiers.length > 0 && (!hasValueBinding || importDeclaration.importAttributes !== null || importDeclaration.leadingCommentsText.trim() !== "" || importDeclaration.trailingCommentsText !== "")) {
2042
2206
  return [
2043
2207
  {
2044
2208
  ...importDeclaration,
@@ -2047,25 +2211,25 @@ function applyTypeImportStyle(importDeclaration, typeImportStyle) {
2047
2211
  ];
2048
2212
  }
2049
2213
  const styledImportDeclarations = [];
2050
- if (typeSpecifiers2.length > 0) {
2214
+ if (typeSpecifiers.length > 0) {
2051
2215
  styledImportDeclarations.push({
2052
2216
  ...importDeclaration,
2053
2217
  isTypeOnly: true,
2054
2218
  defaultBinding: null,
2055
2219
  namespaceBinding: null,
2056
- namedSpecifiers: sortNamedImportSpecifiers(typeSpecifiers2.map((importSpecifier) => ({
2220
+ namedSpecifiers: sortNamedImportSpecifiers(typeSpecifiers.map((importSpecifier) => ({
2057
2221
  ...importSpecifier,
2058
2222
  isTypeOnly: false
2059
2223
  })))
2060
2224
  });
2061
2225
  }
2062
- if (valueSpecifiers2.length > 0 || importDeclaration.defaultBinding !== null || importDeclaration.namespaceBinding !== null) {
2226
+ if (hasValueBinding) {
2063
2227
  let namedValueSpecifiers = null;
2064
- if (valueSpecifiers2.length > 0) {
2065
- namedValueSpecifiers = sortNamedImportSpecifiers(valueSpecifiers2);
2228
+ if (valueSpecifiers.length > 0) {
2229
+ namedValueSpecifiers = sortNamedImportSpecifiers(valueSpecifiers);
2066
2230
  }
2067
2231
  let leadingCommentsText = importDeclaration.leadingCommentsText;
2068
- if (typeSpecifiers2.length > 0) {
2232
+ if (typeSpecifiers.length > 0) {
2069
2233
  leadingCommentsText = "";
2070
2234
  }
2071
2235
  styledImportDeclarations.push({
@@ -2108,11 +2272,19 @@ function renderSortedImportSegment(importDeclarations, sortOptions, pathPatterns
2108
2272
  }
2109
2273
  const styledImportDeclarations = mergedImportDeclarations.flatMap((importDeclaration) => applyTypeImportStyle(importDeclaration, sortOptions.esmImportTypeStyle));
2110
2274
  const unlistedGroupRank = sortOptions.esmImportGroups.length;
2111
- const rankedImportDeclarations = styledImportDeclarations.map((importDeclaration, originalIndex) => ({
2112
- importDeclaration,
2113
- importGroup: classifyImportGroup(importDeclaration.moduleSpecifier, pathPatterns),
2114
- originalIndex
2115
- }));
2275
+ const sortRegionByRequest = new Map;
2276
+ const rankedImportDeclarations = styledImportDeclarations.map((importDeclaration, originalIndex) => {
2277
+ const importRequestKey = getImportRequestKey(importDeclaration);
2278
+ const regionOffset = importDeclaration.isMergeBoundary ? 1 : 0;
2279
+ const sortRegion = (sortRegionByRequest.get(importRequestKey) ?? 0) + regionOffset;
2280
+ sortRegionByRequest.set(importRequestKey, sortRegion + regionOffset);
2281
+ return {
2282
+ importDeclaration,
2283
+ importGroup: classifyImportGroup(importDeclaration.moduleSpecifier, pathPatterns),
2284
+ sortRegion,
2285
+ originalIndex
2286
+ };
2287
+ });
2116
2288
  rankedImportDeclarations.sort((left, right) => {
2117
2289
  const groupRankDifference = (importGroupOrder.get(left.importGroup) ?? unlistedGroupRank) - (importGroupOrder.get(right.importGroup) ?? unlistedGroupRank);
2118
2290
  if (groupRankDifference !== 0) {
@@ -2122,6 +2294,10 @@ function renderSortedImportSegment(importDeclarations, sortOptions, pathPatterns
2122
2294
  if (moduleSpecifierDifference !== 0) {
2123
2295
  return moduleSpecifierDifference;
2124
2296
  }
2297
+ const sortRegionDifference = left.sortRegion - right.sortRegion;
2298
+ if (sortRegionDifference !== 0) {
2299
+ return sortRegionDifference;
2300
+ }
2125
2301
  if (left.importDeclaration.isTypeOnly !== right.importDeclaration.isTypeOnly) {
2126
2302
  if (left.importDeclaration.isTypeOnly) {
2127
2303
  return -1;
@@ -2252,6 +2428,9 @@ function buildImportSortingEdits(sourceText, programStatements, sortedComments,
2252
2428
  }
2253
2429
  const isImportIgnoredByPrettier = isPrettierIgnored(sourceText, declarationRange, sortedComments);
2254
2430
  const leadingCommentsStart = findLeadingCommentsStart(sourceText, declarationRange.start, sortedComments, prettierFilePragmaComment);
2431
+ if (leadingCommentsStart === null) {
2432
+ return [];
2433
+ }
2255
2434
  const trailingComments = getTrailingComments(sourceText, declarationRange.end, sortedComments);
2256
2435
  if (isImportIgnoredByPrettier || trailingComments.isImportSortingBoundary) {
2257
2436
  if (currentSortableSegment.length > 0) {
@@ -2307,14 +2486,92 @@ function buildImportSortingEdits(sourceText, programStatements, sortedComments,
2307
2486
  }
2308
2487
 
2309
2488
  // src/sort-typescript.ts
2489
+ var SCRIPT_FILE_EXTENSION_PATTERN = /\.[cm]?[jt]sx?$/i;
2310
2490
  async function getTsconfigPathPatterns(filePath) {
2311
2491
  if (!filePath) {
2312
2492
  return [];
2313
2493
  }
2314
2494
  try {
2315
- const { getTsconfig } = await import("get-tsconfig");
2316
- const paths = getTsconfig(filePath)?.config.compilerOptions?.paths;
2317
- return paths ? Object.keys(paths) : [];
2495
+ let isFileIncluded = function(project) {
2496
+ try {
2497
+ if (createFilesMatcher(project, isFileSystemCaseSensitive)(absoluteFilePath)) {
2498
+ return true;
2499
+ }
2500
+ const fileExtension = extname(absoluteFilePath);
2501
+ if (!fileExtension || SCRIPT_FILE_EXTENSION_PATTERN.test(fileExtension)) {
2502
+ return false;
2503
+ }
2504
+ const virtualExtension = ".ts";
2505
+ const normalizedFileExtension = isFileSystemCaseSensitive ? fileExtension : fileExtension.toLowerCase();
2506
+ const addVirtualExtension = (patterns) => patterns?.map((pattern) => {
2507
+ const normalizedPattern = isFileSystemCaseSensitive ? pattern : pattern.toLowerCase();
2508
+ return normalizedPattern.endsWith(normalizedFileExtension) ? `${pattern}${virtualExtension}` : pattern;
2509
+ });
2510
+ const virtualConfig = {
2511
+ ...project.config,
2512
+ files: addVirtualExtension(project.config.files),
2513
+ include: addVirtualExtension(project.config.include),
2514
+ exclude: addVirtualExtension(project.config.exclude)
2515
+ };
2516
+ return Boolean(createFilesMatcher({ path: project.path, config: virtualConfig }, isFileSystemCaseSensitive)(`${absoluteFilePath}${virtualExtension}`));
2517
+ } catch {
2518
+ return false;
2519
+ }
2520
+ }, findProjectForFile = function(project) {
2521
+ if (visitedConfigPaths.has(project.path)) {
2522
+ return;
2523
+ }
2524
+ visitedConfigPaths.add(project.path);
2525
+ if (isFileIncluded(project)) {
2526
+ return project;
2527
+ }
2528
+ for (const reference of project.config.references ?? []) {
2529
+ if (typeof reference.path !== "string") {
2530
+ continue;
2531
+ }
2532
+ let referencedProject;
2533
+ try {
2534
+ const referencePath = resolve(dirname(project.path), reference.path);
2535
+ const configPath = extname(referencePath).toLowerCase() === ".json" ? referencePath : join(referencePath, "tsconfig.json");
2536
+ referencedProject = {
2537
+ path: configPath,
2538
+ config: parseTsconfig(configPath, cache)
2539
+ };
2540
+ } catch {
2541
+ continue;
2542
+ }
2543
+ const selectedProject = findProjectForFile(referencedProject);
2544
+ if (selectedProject) {
2545
+ return selectedProject;
2546
+ }
2547
+ }
2548
+ return;
2549
+ };
2550
+ const { createFilesMatcher, getTsconfig, parseTsconfig } = await import("get-tsconfig");
2551
+ const { dirname, extname, join, resolve } = await import("node:path");
2552
+ const cache = new Map;
2553
+ const absoluteFilePath = resolve(filePath);
2554
+ const isFileSystemCaseSensitive = !["darwin", "win32"].includes(process.platform);
2555
+ const nearestProject = getTsconfig(absoluteFilePath, "tsconfig.json", cache);
2556
+ if (!nearestProject) {
2557
+ return [];
2558
+ }
2559
+ const visitedConfigPaths = new Set;
2560
+ let currentProject = nearestProject;
2561
+ while (currentProject) {
2562
+ const selectedProject = findProjectForFile(currentProject);
2563
+ if (selectedProject) {
2564
+ const paths = selectedProject.config.compilerOptions?.paths;
2565
+ return paths ? Object.keys(paths) : [];
2566
+ }
2567
+ const configDirectory = dirname(currentProject.path);
2568
+ const parentDirectory = dirname(configDirectory);
2569
+ if (parentDirectory === configDirectory) {
2570
+ break;
2571
+ }
2572
+ currentProject = getTsconfig(parentDirectory, "tsconfig.json", cache);
2573
+ }
2574
+ return [];
2318
2575
  } catch {
2319
2576
  return [];
2320
2577
  }
@@ -2346,7 +2603,10 @@ async function sortTypeScript(sourceText, prettierOptions, parser) {
2346
2603
  return sourceText;
2347
2604
  }
2348
2605
  try {
2349
- await parser.parse(sortedText, prettierOptions);
2606
+ const sortedAst = await parser.parse(sortedText, prettierOptions);
2607
+ if (!updateCursorOffset(sourceText, sortedText, parserAst, sortedAst, prettierOptions)) {
2608
+ return sourceText;
2609
+ }
2350
2610
  return sortedText;
2351
2611
  } catch {
2352
2612
  return sourceText;
@@ -2354,6 +2614,7 @@ async function sortTypeScript(sourceText, prettierOptions, parser) {
2354
2614
  }
2355
2615
 
2356
2616
  // src/index.ts
2617
+ var IS_RANGE_FORMATTING = Symbol("isRangeFormatting");
2357
2618
  function wrapParserPreprocess(parser, preprocessTransform) {
2358
2619
  return {
2359
2620
  ...parser,
@@ -2362,6 +2623,10 @@ function wrapParserPreprocess(parser, preprocessTransform) {
2362
2623
  if (parser.preprocess) {
2363
2624
  transformedText = await parser.preprocess(sourceText, prettierOptions);
2364
2625
  }
2626
+ if (prettierOptions[IS_RANGE_FORMATTING] || prettierOptions.rangeStart > 0 || prettierOptions.rangeEnd < sourceText.length) {
2627
+ prettierOptions[IS_RANGE_FORMATTING] = true;
2628
+ return transformedText;
2629
+ }
2365
2630
  return preprocessTransform(transformedText, prettierOptions, parser);
2366
2631
  }
2367
2632
  };
@@ -2387,6 +2652,6 @@ var sortPlugin = {
2387
2652
  };
2388
2653
  var src_default = sortPlugin;
2389
2654
  export {
2390
- options,
2391
- src_default as default
2655
+ src_default as default,
2656
+ options
2392
2657
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prettier-plugin-sort",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "A Prettier plugin for sorting ES module imports, export specifiers, and fields in package.json and tsconfig.json.",
5
5
  "keywords": [
6
6
  "imports",