Repository: JoshuaKGoldberg/eslint-plugin-package-json (ts)

Issue:
🚀 Feature: [sort-collections] Support nested properties
### Feature Request Checklist

- [x] I have pulled the latest `main` branch of the repository.
- [x] I have [searched for related issues](https://github.com/JoshuaKGoldberg/eslint-plugin-package-json/issues?q=is%3Aissue) and found none that matched my issue.

### Overview

Filing a backing issue for #1048: it'd be nice if the array option for [`sort-collections`](https://github.com/JoshuaKGoldberg/eslint-plugin-package-json/blob/bae94b56e23f60e5f38c5e4b5260737c8740c81b/docs/rules/sort-collections.md) supported nested properties. As @andreww2012 noted:

> This can be useful for nested properties ordering, such as `pnpm.patchedDependencies`.

### Additional Info

🗂️

Pull request description:
 feat(sort-collections): support nested properties 
<!-- 👋 Hi, thanks for sending a PR to eslint-plugin-package-json! 🗂
Please fill out all fields below and make sure each item is true and [x] checked.
Otherwise we may not be able to review your PR. -->

## PR Checklist

- [x] Addresses an existing open issue: fixes #1093
- [x] That issue was marked as [`status: accepting prs`](https://github.com/JoshuaKGoldberg/eslint-plugin-package-json/issues?q=is%3Aopen+is%3Aissue+label%3A%22status%3A+accepting+prs%22)
- [x] Steps in [CONTRIBUTING.md](https://github.com/JoshuaKGoldberg/eslint-plugin-package-json/blob/main/.github/CONTRIBUTING.md) were taken

## Overview

Hello! This PR adds the ability for `sort-collections` rule to accept a nested property using a dot notation. This can be useful for nested properties ordering, such as `pnpm.patchedDependencies`.


Patch:
diff --git a/docs/rules/sort-collections.md b/docs/rules/sort-collections.md
index 425ea4f..f032d8d 100644
--- a/docs/rules/sort-collections.md
+++ b/docs/rules/sort-collections.md
@@ -79,14 +79,18 @@ Note that in lexical ordering, `lodash` comes before `lodash.debounce`.
 
 ### Options
 
-Pass an array of top-level package properties to lint sorting on those collections.
+Pass an array of package properties to lint sorting on those collections.
+To specify a nested property, use dot notation.
 All of their values must be objects.
 
-Example of only sorting devDependencies:
+Example of only sorting `devDependencies` and `pnpm.patchedDependencies`:
 
 ```json
 {
-	"package-json/sort-collections": ["error", ["devDependencies"]]
+	"package-json/sort-collections": [
+		"error",
+		["devDependencies", "pnpm.patchedDependencies"]
+	]
 }
 ```
 
@@ -94,13 +98,15 @@ Defaults:
 
 ```json
 [
-	"scripts",
-	"devDependencies",
+	"config",
 	"dependencies",
-	"peerDependencies",
+	"devDependencies",
+	"exports",
+	"optionalDependencies",
 	"overrides",
-	"config",
-	"exports"
+	"peerDependencies",
+	"peerDependenciesMeta",
+	"scripts"
 ]
 ```
 
diff --git a/eslint.config.js b/eslint.config.ts
similarity index 86%
rename from eslint.config.js
rename to eslint.config.ts
index 0fdbaa3..e2db274 100644
--- a/eslint.config.js
+++ b/eslint.config.ts
@@ -11,6 +11,8 @@ import * as regexp from "eslint-plugin-regexp";
 import yml from "eslint-plugin-yml";
 import tseslint from "typescript-eslint";
 
+import packageJson from "./src/index.js";
+
 export default tseslint.config(
 	{
 		ignores: [
@@ -26,7 +28,9 @@ export default tseslint.config(
 	},
 	{ linterOptions: { reportUnusedDisableDirectives: "error" } },
 	eslint.configs.recommended,
+	// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access -- https://github.com/eslint-community/eslint-plugin-eslint-comments/issues/214
 	comments.recommended,
+	// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access -- https://github.com/eslint-community/eslint-plugin-eslint-plugin/issues/487
 	eslintPlugin.configs["flat/recommended"],
 	jsdoc.configs["flat/contents-typescript-error"],
 	jsdoc.configs["flat/logical-typescript-error"],
@@ -111,4 +115,8 @@ export default tseslint.config(
 			"n/no-unsupported-features/node-builtins": "off",
 		},
 	},
+	{
+		extends: [packageJson.configs.recommended],
+		files: ["package.json"],
+	},
 );
diff --git a/package.json b/package.json
index aa4079a..e74386d 100644
--- a/package.json
+++ b/package.json
@@ -81,6 +81,7 @@
 		"eslint-plugin-regexp": "2.9.0",
 		"eslint-plugin-yml": "1.18.0",
 		"husky": "9.1.7",
+		"jiti": "2.4.2",
 		"jsonc-eslint-parser": "2.4.0",
 		"knip": "5.60.0",
 		"lint-staged": "16.1.0",
@@ -94,7 +95,7 @@
 		"sentences-per-line": "0.3.0",
 		"tsup": "8.5.0",
 		"typescript": "5.8.2",
-		"typescript-eslint": "8.33.0",
+		"typescript-eslint": "8.34.0",
 		"vitest": "3.2.0"
 	},
 	"peerDependencies": {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index eebd311..18c5759 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -111,6 +111,9 @@ importers:
       husky:
         specifier: 9.1.7
         version: 9.1.7
+      jiti:
+        specifier: 2.4.2
+        version: 2.4.2
       jsonc-eslint-parser:
         specifier: 2.4.0
         version: 2.4.0
@@ -151,8 +154,8 @@ importers:
         specifier: 5.8.2
         version: 5.8.2
       typescript-eslint:
-        specifier: 8.33.0
-        version: 8.33.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.2)
+        specifier: 8.34.0
+        version: 8.34.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.2)
       vitest:
         specifier: 3.2.0
         version: 3.2.0(@types/debug@4.1.12)(@types/node@22.15.0)(jiti@2.4.2)(yaml@2.8.0)
@@ -1279,85 +1282,54 @@ packages:
   '@types/validate-npm-package-name@4.0.2':
     resolution: {integrity: sha512-lrpDziQipxCEeK5kWxvljWYhUvOiB2A9izZd9B2AFarYAkqZshb4lPbRs7zKEic6eGtH8V/2qJW+dPp9OtF6bw==}
 
-  '@typescript-eslint/eslint-plugin@8.33.0':
-    resolution: {integrity: sha512-CACyQuqSHt7ma3Ns601xykeBK/rDeZa3w6IS6UtMQbixO5DWy+8TilKkviGDH6jtWCo8FGRKEK5cLLkPvEammQ==}
+  '@typescript-eslint/eslint-plugin@8.34.0':
+    resolution: {integrity: sha512-QXwAlHlbcAwNlEEMKQS2RCgJsgXrTJdjXT08xEgbPFa2yYQgVjBymxP5DrfrE7X7iodSzd9qBUHUycdyVJTW1w==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
     peerDependencies:
-      '@typescript-eslint/parser': ^8.33.0
+      '@typescript-eslint/parser': ^8.34.0
       eslint: ^8.57.0 || ^9.0.0
       typescript: '>=4.8.4 <5.9.0'
 
-  '@typescript-eslint/parser@8.33.0':
-    resolution: {integrity: sha512-JaehZvf6m0yqYp34+RVnihBAChkqeH+tqqhS0GuX1qgPpwLvmTPheKEs6OeCK6hVJgXZHJ2vbjnC9j119auStQ==}
+  '@typescript-eslint/parser@8.34.0':
+    resolution: {integrity: sha512-vxXJV1hVFx3IXz/oy2sICsJukaBrtDEQSBiV48/YIV5KWjX1dO+bcIr/kCPrW6weKXvsaGKFNlwH0v2eYdRRbA==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
     peerDependencies:
       eslint: ^8.57.0 || ^9.0.0
       typescript: '>=4.8.4 <5.9.0'
 
-  '@typescript-eslint/project-service@8.33.0':
-    resolution: {integrity: sha512-d1hz0u9l6N+u/gcrk6s6gYdl7/+pp8yHheRTqP6X5hVDKALEaTn8WfGiit7G511yueBEL3OpOEpD+3/MBdoN+A==}
-    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
   '@typescript-eslint/project-service@8.34.0':
     resolution: {integrity: sha512-iEgDALRf970/B2YExmtPMPF54NenZUf4xpL3wsCRx/lgjz6ul/l13R81ozP/ZNuXfnLCS+oPmG7JIxfdNYKELw==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
     peerDependencies:
       typescript: '>=4.8.4 <5.9.0'
 
-  '@typescript-eslint/scope-manager@8.33.0':
-    resolution: {integrity: sha512-LMi/oqrzpqxyO72ltP+dBSP6V0xiUb4saY7WLtxSfiNEBI8m321LLVFU9/QDJxjDQG9/tjSqKz/E3380TEqSTw==}
-    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
   '@typescript-eslint/scope-manager@8.34.0':
     resolution: {integrity: sha512-9Ac0X8WiLykl0aj1oYQNcLZjHgBojT6cW68yAgZ19letYu+Hxd0rE0veI1XznSSst1X5lwnxhPbVdwjDRIomRw==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
-  '@typescript-eslint/tsconfig-utils@8.33.0':
-    resolution: {integrity: sha512-sTkETlbqhEoiFmGr1gsdq5HyVbSOF0145SYDJ/EQmXHtKViCaGvnyLqWFFHtEXoS0J1yU8Wyou2UGmgW88fEug==}
-    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-    peerDependencies:
-      typescript: '>=4.8.4 <5.9.0'
-
   '@typescript-eslint/tsconfig-utils@8.34.0':
     resolution: {integrity: sha512-+W9VYHKFIzA5cBeooqQxqNriAP0QeQ7xTiDuIOr71hzgffm3EL2hxwWBIIj4GuofIbKxGNarpKqIq6Q6YrShOA==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
     peerDependencies:
       typescript: '>=4.8.4 <5.9.0'
 
-  '@typescript-eslint/type-utils@8.33.0':
-    resolution: {integrity: sha512-lScnHNCBqL1QayuSrWeqAL5GmqNdVUQAAMTaCwdYEdWfIrSrOGzyLGRCHXcCixa5NK6i5l0AfSO2oBSjCjf4XQ==}
+  '@typescript-eslint/type-utils@8.34.0':
+    resolution: {integrity: sha512-n7zSmOcUVhcRYC75W2pnPpbO1iwhJY3NLoHEtbJwJSNlVAZuwqu05zY3f3s2SDWWDSo9FdN5szqc73DCtDObAg==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
     peerDependencies:
       eslint: ^8.57.0 || ^9.0.0
       typescript: '>=4.8.4 <5.9.0'
 
-  '@typescript-eslint/types@8.33.0':
-    resolution: {integrity: sha512-DKuXOKpM5IDT1FA2g9x9x1Ug81YuKrzf4mYX8FAVSNu5Wo/LELHWQyM1pQaDkI42bX15PWl0vNPt1uGiIFUOpg==}
-    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
   '@typescript-eslint/types@8.34.0':
     resolution: {integrity: sha512-9V24k/paICYPniajHfJ4cuAWETnt7Ssy+R0Rbcqo5sSFr3QEZ/8TSoUi9XeXVBGXCaLtwTOKSLGcInCAvyZeMA==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
-  '@typescript-eslint/typescript-estree@8.33.0':
-    resolution: {integrity: sha512-vegY4FQoB6jL97Tu/lWRsAiUUp8qJTqzAmENH2k59SJhw0Th1oszb9Idq/FyyONLuNqT1OADJPXfyUNOR8SzAQ==}
-    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-    peerDependencies:
-      typescript: '>=4.8.4 <5.9.0'
-
   '@typescript-eslint/typescript-estree@8.34.0':
     resolution: {integrity: sha512-rOi4KZxI7E0+BMqG7emPSK1bB4RICCpF7QD3KCLXn9ZvWoESsOMlHyZPAHyG04ujVplPaHbmEvs34m+wjgtVtg==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
     peerDependencies:
       typescript: '>=4.8.4 <5.9.0'
 
-  '@typescript-eslint/utils@8.33.0':
-    resolution: {integrity: sha512-lPFuQaLA9aSNa7D5u2EpRiqdAUhzShwGg/nhpBlc4GR6kcTABttCuyjFs8BcEZ8VWrjCBof/bePhP3Q3fS+Yrw==}
-    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-    peerDependencies:
-      eslint: ^8.57.0 || ^9.0.0
-      typescript: '>=4.8.4 <5.9.0'
-
   '@typescript-eslint/utils@8.34.0':
     resolution: {integrity: sha512-8L4tWatGchV9A1cKbjaavS6mwYwp39jql8xUmIIKJdm+qiaeHy5KMKlBrf30akXAWBzn2SqKsNOtSENWUwg7XQ==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -1365,10 +1337,6 @@ packages:
       eslint: ^8.57.0 || ^9.0.0
       typescript: '>=4.8.4 <5.9.0'
 
-  '@typescript-eslint/visitor-keys@8.33.0':
-    resolution: {integrity: sha512-7RW7CMYoskiz5OOGAWjJFxgb7c5UNjTG292gYhWeOAcFmYCtVCSqjqSBj5zMhxbXo2JOW95YYrUWJfU0zrpaGQ==}
-    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
   '@typescript-eslint/visitor-keys@8.34.0':
     resolution: {integrity: sha512-qHV7pW7E85A0x6qyrFn+O+q1k1p3tQCsqIZ1KZ5ESLXY57aTvUd3/a4rdPTeXisvhXn2VQG0VSKUqs8KHF2zcA==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -3970,8 +3938,8 @@ packages:
   typedarray@0.0.6:
     resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==}
 
-  typescript-eslint@8.33.0:
-    resolution: {integrity: sha512-5YmNhF24ylCsvdNW2oJwMzTbaeO4bg90KeGtMjUw0AGtHksgEPLRTUil+coHwCfiu4QjVJFnjp94DmU6zV7DhQ==}
+  typescript-eslint@8.34.0:
+    resolution: {integrity: sha512-MRpfN7uYjTrTGigFCt8sRyNqJFhjN0WwZecldaqhWm+wy0gaRt8Edb/3cuUy0zdq2opJWT6iXINKAtewnDOltQ==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
     peerDependencies:
       eslint: ^8.57.0 || ^9.0.0
@@ -5304,14 +5272,14 @@ snapshots:
 
   '@types/validate-npm-package-name@4.0.2': {}
 
-  '@typescript-eslint/eslint-plugin@8.33.0(@typescript-eslint/parser@8.33.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.2)':
+  '@typescript-eslint/eslint-plugin@8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.2)':
     dependencies:
       '@eslint-community/regexpp': 4.12.1
-      '@typescript-eslint/parser': 8.33.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.2)
-      '@typescript-eslint/scope-manager': 8.33.0
-      '@typescript-eslint/type-utils': 8.33.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.2)
-      '@typescript-eslint/utils': 8.33.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.2)
-      '@typescript-eslint/visitor-keys': 8.33.0
+      '@typescript-eslint/parser': 8.34.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.2)
+      '@typescript-eslint/scope-manager': 8.34.0
+      '@typescript-eslint/type-utils': 8.34.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.2)
+      '@typescript-eslint/utils': 8.34.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.2)
+      '@typescript-eslint/visitor-keys': 8.34.0
       eslint: 9.28.0(jiti@2.4.2)
       graphemer: 1.4.0
       ignore: 7.0.4
@@ -5321,27 +5289,18 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/parser@8.33.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.2)':
+  '@typescript-eslint/parser@8.34.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.2)':
     dependencies:
-      '@typescript-eslint/scope-manager': 8.33.0
-      '@typescript-eslint/types': 8.33.0
-      '@typescript-eslint/typescript-estree': 8.33.0(typescript@5.8.2)
-      '@typescript-eslint/visitor-keys': 8.33.0
+      '@typescript-eslint/scope-manager': 8.34.0
+      '@typescript-eslint/types': 8.34.0
+      '@typescript-eslint/typescript-estree': 8.34.0(typescript@5.8.2)
+      '@typescript-eslint/visitor-keys': 8.34.0
       debug: 4.4.1
       eslint: 9.28.0(jiti@2.4.2)
       typescript: 5.8.2
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/project-service@8.33.0(typescript@5.8.2)':
-    dependencies:
-      '@typescript-eslint/tsconfig-utils': 8.34.0(typescript@5.8.2)
-      '@typescript-eslint/types': 8.34.0
-      debug: 4.4.1
-    transitivePeerDependencies:
-      - supports-color
-      - typescript
-
   '@typescript-eslint/project-service@8.34.0(typescript@5.8.2)':
     dependencies:
       '@typescript-eslint/tsconfig-utils': 8.34.0(typescript@5.8.2)
@@ -5351,28 +5310,19 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/scope-manager@8.33.0':
-    dependencies:
-      '@typescript-eslint/types': 8.33.0
-      '@typescript-eslint/visitor-keys': 8.33.0
-
   '@typescript-eslint/scope-manager@8.34.0':
     dependencies:
       '@typescript-eslint/types': 8.34.0
       '@typescript-eslint/visitor-keys': 8.34.0
 
-  '@typescript-eslint/tsconfig-utils@8.33.0(typescript@5.8.2)':
-    dependencies:
-      typescript: 5.8.2
-
   '@typescript-eslint/tsconfig-utils@8.34.0(typescript@5.8.2)':
     dependencies:
       typescript: 5.8.2
 
-  '@typescript-eslint/type-utils@8.33.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.2)':
+  '@typescript-eslint/type-utils@8.34.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.2)':
     dependencies:
-      '@typescript-eslint/typescript-estree': 8.33.0(typescript@5.8.2)
-      '@typescript-eslint/utils': 8.33.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.2)
+      '@typescript-eslint/typescript-estree': 8.34.0(typescript@5.8.2)
+      '@typescript-eslint/utils': 8.34.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.2)
       debug: 4.4.1
       eslint: 9.28.0(jiti@2.4.2)
       ts-api-utils: 2.1.0(typescript@5.8.2)
@@ -5380,26 +5330,8 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/types@8.33.0': {}
-
   '@typescript-eslint/types@8.34.0': {}
 
-  '@typescript-eslint/typescript-estree@8.33.0(typescript@5.8.2)':
-    dependencies:
-      '@typescript-eslint/project-service': 8.33.0(typescript@5.8.2)
-      '@typescript-eslint/tsconfig-utils': 8.33.0(typescript@5.8.2)
-      '@typescript-eslint/types': 8.33.0
-      '@typescript-eslint/visitor-keys': 8.33.0
-      debug: 4.4.1
-      fast-glob: 3.3.3
-      is-glob: 4.0.3
-      minimatch: 9.0.5
-      semver: 7.7.2
-      ts-api-utils: 2.1.0(typescript@5.8.2)
-      typescript: 5.8.2
-    transitivePeerDependencies:
-      - supports-color
-
   '@typescript-eslint/typescript-estree@8.34.0(typescript@5.8.2)':
     dependencies:
       '@typescript-eslint/project-service': 8.34.0(typescript@5.8.2)
@@ -5416,17 +5348,6 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/utils@8.33.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.2)':
-    dependencies:
-      '@eslint-community/eslint-utils': 4.7.0(eslint@9.28.0(jiti@2.4.2))
-      '@typescript-eslint/scope-manager': 8.33.0
-      '@typescript-eslint/types': 8.33.0
-      '@typescript-eslint/typescript-estree': 8.33.0(typescript@5.8.2)
-      eslint: 9.28.0(jiti@2.4.2)
-      typescript: 5.8.2
-    transitivePeerDependencies:
-      - supports-color
-
   '@typescript-eslint/utils@8.34.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.2)':
     dependencies:
       '@eslint-community/eslint-utils': 4.7.0(eslint@9.28.0(jiti@2.4.2))
@@ -5438,11 +5359,6 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/visitor-keys@8.33.0':
-    dependencies:
-      '@typescript-eslint/types': 8.33.0
-      eslint-visitor-keys: 4.2.0
-
   '@typescript-eslint/visitor-keys@8.34.0':
     dependencies:
       '@typescript-eslint/types': 8.34.0
@@ -8448,11 +8364,11 @@ snapshots:
 
   typedarray@0.0.6: {}
 
-  typescript-eslint@8.33.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.2):
+  typescript-eslint@8.34.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.2):
     dependencies:
-      '@typescript-eslint/eslint-plugin': 8.33.0(@typescript-eslint/parser@8.33.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.2)
-      '@typescript-eslint/parser': 8.33.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.2)
-      '@typescript-eslint/utils': 8.33.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.2)
+      '@typescript-eslint/eslint-plugin': 8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.2)
+      '@typescript-eslint/parser': 8.34.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.2)
+      '@typescript-eslint/utils': 8.34.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.2)
       eslint: 9.28.0(jiti@2.4.2)
       typescript: 5.8.2
     transitivePeerDependencies:
diff --git a/src/rules/sort-collections.ts b/src/rules/sort-collections.ts
index 6ad27aa..d0ef5e6 100644
--- a/src/rules/sort-collections.ts
+++ b/src/rules/sort-collections.ts
@@ -1,125 +1,152 @@
+import type { AST as JsonAST } from "jsonc-eslint-parser";
+
 import * as ESTree from "estree";
-import { AST } from "jsonc-eslint-parser";
 
 import { createRule } from "../createRule.js";
 
-const defaultCollections = [
-	"scripts",
-	"devDependencies",
-	"dependencies",
-	"peerDependencies",
-	"overrides",
+const defaultCollections = new Set([
 	"config",
+	"dependencies",
+	"devDependencies",
 	"exports",
-];
+	"optionalDependencies",
+	"overrides",
+	"peerDependencies",
+	"peerDependenciesMeta",
+	"scripts",
+]);
 
 type Options = string[];
 
 export const rule = createRule<Options>({
 	create(context) {
-		const toSort = context.options[0] || defaultCollections;
+		const toSort = context.options[0]
+			? new Set(context.options[0])
+			: defaultCollections;
+
 		return {
 			"JSONProperty:exit"(node) {
-				const { key, value } = node as AST.JSONProperty & {
-					key: AST.JSONStringLiteral;
-				};
+				const { key: nodeKey, value: collection } = node;
 
-				const collection = value;
 				if (
-					collection.type === "JSONObjectExpression" &&
-					toSort.includes(key.value)
+					nodeKey.type !== "JSONLiteral" ||
+					collection.type !== "JSONObjectExpression"
+				) {
+					return;
+				}
+
+				const keyPartsReversed = [nodeKey.value];
+				for (
+					let currNode: JsonAST.JSONNode | null | undefined =
+						node.parent;
+					// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+					currNode;
+					currNode = currNode.parent
 				) {
-					const currentOrder = collection.properties;
-					const scripts = new Set(
-						currentOrder.map(
-							(prop) => (prop.key as AST.JSONStringLiteral).value,
-						),
-					);
-
-					const desiredOrder = currentOrder.slice().sort((a, b) => {
-						let aKey = (a.key as AST.JSONStringLiteral).value;
-						let bKey = (b.key as AST.JSONStringLiteral).value;
-
-						if (key.value !== "scripts") {
-							return aKey > bKey ? 1 : -1;
-						} else {
-							let modifier = 0;
-
-							if (
-								aKey.startsWith("pre") &&
-								scripts.has(aKey.substring(3))
-							) {
-								aKey = aKey.substring(3);
-								modifier -= 1;
-							} else if (
-								aKey.startsWith("post") &&
-								scripts.has(aKey.substring(4))
-							) {
-								aKey = aKey.substring(4);
-								modifier += 1;
-							}
-
-							if (
-								bKey.startsWith("pre") &&
-								scripts.has(bKey.substring(3))
-							) {
-								bKey = bKey.substring(3);
-								modifier += 1;
-							} else if (
-								bKey.startsWith("post") &&
-								scripts.has(bKey.substring(4))
-							) {
-								bKey = bKey.substring(4);
-								modifier -= 1;
-							}
-
-							if (aKey === bKey) {
-								return modifier;
-							}
-
-							return aKey > bKey ? 1 : -1;
-						}
-					});
 					if (
-						currentOrder.some(
-							(property, i) => desiredOrder[i] !== property,
-						)
+						currNode.type === "JSONProperty" &&
+						currNode.key.type === "JSONLiteral"
 					) {
-						context.report({
-							data: {
-								key: key.value,
-							},
-							fix(fixer) {
-								return fixer.replaceText(
-									collection as unknown as ESTree.Node,
-									JSON.stringify(
-										desiredOrder.reduce<
-											Record<string, unknown>
-										>((out, property) => {
-											out[
-												(
-													property.key as AST.JSONStringLiteral
-												).value
-											] = JSON.parse(
-												context.sourceCode.getText(
-													property.value as unknown as ESTree.Node,
-												),
-											);
-											return out;
-										}, {}),
-										null,
-										2,
-									)
-										.split("\n")
-										.join("\n  "), // nest indents
-								);
-							},
-							loc: collection.loc,
-							messageId: "notAlphabetized",
-							node: node as unknown as ESTree.Node,
-						});
+						keyPartsReversed.push(currNode.key.value);
+					} else if (currNode.type === "JSONArrayExpression") {
+						return;
 					}
 				}
+				const key = keyPartsReversed.reverse().join(".");
+				if (!toSort.has(key)) {
+					return;
+				}
+
+				const currentOrder = collection.properties;
+				const properties = new Set(
+					currentOrder.map(
+						(prop) => (prop.key as JsonAST.JSONStringLiteral).value,
+					),
+				);
+
+				const desiredOrder = currentOrder.slice().sort((a, b) => {
+					let aKey = (a.key as JsonAST.JSONStringLiteral).value;
+					let bKey = (b.key as JsonAST.JSONStringLiteral).value;
+
+					if (keyPartsReversed.at(-1) !== "scripts") {
+						return aKey > bKey ? 1 : -1;
+					} else {
+						let modifier = 0;
+
+						if (
+							aKey.startsWith("pre") &&
+							properties.has(aKey.substring(3))
+						) {
+							aKey = aKey.substring(3);
+							modifier -= 1;
+						} else if (
+							aKey.startsWith("post") &&
+							properties.has(aKey.substring(4))
+						) {
+							aKey = aKey.substring(4);
+							modifier += 1;
+						}
+
+						if (
+							bKey.startsWith("pre") &&
+							properties.has(bKey.substring(3))
+						) {
+							bKey = bKey.substring(3);
+							modifier += 1;
+						} else if (
+							bKey.startsWith("post") &&
+							properties.has(bKey.substring(4))
+						) {
+							bKey = bKey.substring(4);
+							modifier -= 1;
+						}
+
+						if (aKey === bKey) {
+							return modifier;
+						}
+
+						return aKey > bKey ? 1 : -1;
+					}
+				});
+				if (
+					currentOrder.some(
+						(property, i) => desiredOrder[i] !== property,
+					)
+				) {
+					context.report({
+						data: {
+							key,
+						},
+						fix(fixer) {
+							return fixer.replaceText(
+								collection as unknown as ESTree.Node,
+								JSON.stringify(
+									desiredOrder.reduce<
+										Record<string, unknown>
+									>((out, property) => {
+										out[
+											(
+												property.key as JsonAST.JSONStringLiteral
+											).value
+										] = JSON.parse(
+											context.sourceCode.getText(
+												property.value as unknown as ESTree.Node,
+											),
+										);
+										return out;
+									}, {}),
+									null,
+									2,
+								)
+									.split("\n")
+									.join("\n  "), // nest indents
+							);
+						},
+						loc: collection.loc,
+						messageId: "notAlphabetized",
+						node: node as unknown as ESTree.Node,
+					});
+				}
 			},
 		};
 	},


Test patch:
diff --git a/src/tests/index.test.ts b/src/tests/index.test.ts
index 2673162..c279cd2 100644
--- a/src/tests/index.test.ts
+++ b/src/tests/index.test.ts
@@ -10,6 +10,7 @@ describe("configs", () => {
 		const eslint = new ESLint({
 			baseConfig: plugin.configs.recommended as Linter.Config,
 			fix: true,
+			overrideConfigFile: true,
 		});
 		const code = await readFile(
 			resolve(import.meta.filename, "../../../package.json"),
diff --git a/src/tests/rules/sort-collections.test.ts b/src/tests/rules/sort-collections.test.ts
index d950a7d..5967e37 100644
--- a/src/tests/rules/sort-collections.test.ts
+++ b/src/tests/rules/sort-collections.test.ts
@@ -149,6 +149,64 @@ ruleTester.run("sort-collections", rule, {
     },
     "./package.json": "./package.json"
   }
+}`,
+		},
+		{
+			code: `{
+	"pnpm": {
+		"patchedDependencies": {
+			"typescript@4.8.4": "patches/typescript@4.8.4.patch",
+			"eslint-plugin-package-json@0.31.0": "patches/eslint-plugin-package-json@0.31.0.patch"
+		}
+	}
+}`,
+			errors: [
+				{
+					data: { key: "pnpm.patchedDependencies" },
+					messageId: "notAlphabetized",
+					type: "JSONProperty",
+				},
+			],
+			filename: "package.json",
+			options: [["pnpm.patchedDependencies"]],
+			output: `{
+	"pnpm": {
+		"patchedDependencies": {
+    "eslint-plugin-package-json@0.31.0": "patches/eslint-plugin-package-json@0.31.0.patch",
+    "typescript@4.8.4": "patches/typescript@4.8.4.patch"
+  }
+	}
+}`,
+		},
+		{
+			code: `{
+	"pnpm": {
+		"peerDependencyRules": {
+            "allowedVersions": {
+                "vue": "2",
+                "react": "17"
+            }
+		}
+	}
+}`,
+			errors: [
+				{
+					data: { key: "pnpm.peerDependencyRules.allowedVersions" },
+					messageId: "notAlphabetized",
+					type: "JSONProperty",
+				},
+			],
+			filename: "package.json",
+			options: [["pnpm.peerDependencyRules.allowedVersions"]],
+			output: `{
+	"pnpm": {
+		"peerDependencyRules": {
+            "allowedVersions": {
+    "react": "17",
+    "vue": "2"
+  }
+		}
+	}
 }`,
 		},
 	],
@@ -214,5 +272,77 @@ ruleTester.run("sort-collections", rule, {
 	}
 }`,
 		},
+		{
+			code: `{
+	"nested": {
+		"devDependencies": {
+			"typescript": "4.8.4",
+			"eslint-plugin-package-json": "0.31.0"
+		}
+	}
+}`,
+		},
+		{
+			code: `{
+	"pnpm": {
+		"patchedDependencies": {
+			"eslint-plugin-package-json@0.31.0": "patches/eslint-plugin-package-json@0.31.0.patch",
+			"typescript@4.8.4": "patches/typescript@4.8.4.patch"
+		}
+	}
+}`,
+		},
+		{
+			code: `{
+	"pnpm": {
+		"peerDependencyRules": {
+            "allowedVersions": {
+                "vue": "2",
+                "react": "17"
+            }
+		}
+	}
+}`,
+		},
+		{
+			code: `{
+	"pnpm": {
+		"peerDependencyRules": {
+            "allowedVersions": {
+                "react": "17",
+                "vue": "2"
+            }
+		}
+	}
+}`,
+			options: [["pnpm.peerDependencyRules.allowedVersions"]],
+		},
+
+		{
+			code: `{
+  "foo": [
+    {
+      "bar": {
+        "b": 2,
+        "a": 1
+      }
+    }
+  ]
+}`,
+			options: [["foo.bar"]],
+		},
+		{
+			code: `{
+  "foo": [
+    {
+      "bar": {
+        "b": 2,
+        "a": 1
+      }
+    }
+  ]
+}`,
+			options: [["foo.0.bar"]],
+		},
 	],
 });