eslint-plugin-package-json 0.58.0 → 0.59.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/CHANGELOG.md CHANGED
@@ -1,11 +1,17 @@
1
1
  # Changelog
2
2
 
3
- # [0.58.0](https://github.com/JoshuaKGoldberg/eslint-plugin-package-json/compare/v0.57.0...v0.58.0) (2025-10-22)
3
+ # [0.59.0](https://github.com/JoshuaKGoldberg/eslint-plugin-package-json/compare/v0.58.0...v0.59.0) (2025-10-25)
4
+
5
+
6
+ ### Features
7
+
8
+ * **exports-subpaths-style:** add new rule ([#1328](https://github.com/JoshuaKGoldberg/eslint-plugin-package-json/issues/1328)) ([d1a82ed](https://github.com/JoshuaKGoldberg/eslint-plugin-package-json/commit/d1a82edf78b7b4bd446018f1ea3bb77e1bc9c772)), closes [#1322](https://github.com/JoshuaKGoldberg/eslint-plugin-package-json/issues/1322)
4
9
 
10
+ # [0.58.0](https://github.com/JoshuaKGoldberg/eslint-plugin-package-json/compare/v0.57.0...v0.58.0) (2025-10-22)
5
11
 
6
12
  ### Features
7
13
 
8
- * mark `legacy-recommended` config deprecated ([#1331](https://github.com/JoshuaKGoldberg/eslint-plugin-package-json/issues/1331)) ([5e20411](https://github.com/JoshuaKGoldberg/eslint-plugin-package-json/commit/5e204118f4b6ae4a50f9a50a1c2a64a5bf84a5a7)), closes [#1329](https://github.com/JoshuaKGoldberg/eslint-plugin-package-json/issues/1329) [#1253](https://github.com/JoshuaKGoldberg/eslint-plugin-package-json/issues/1253)
14
+ - mark `legacy-recommended` config deprecated ([#1331](https://github.com/JoshuaKGoldberg/eslint-plugin-package-json/issues/1331)) ([5e20411](https://github.com/JoshuaKGoldberg/eslint-plugin-package-json/commit/5e204118f4b6ae4a50f9a50a1c2a64a5bf84a5a7)), closes [#1329](https://github.com/JoshuaKGoldberg/eslint-plugin-package-json/issues/1329) [#1253](https://github.com/JoshuaKGoldberg/eslint-plugin-package-json/issues/1253)
9
15
 
10
16
  # [0.57.0](https://github.com/JoshuaKGoldberg/eslint-plugin-package-json/compare/v0.56.4...v0.57.0) (2025-10-16)
11
17
 
package/README.md CHANGED
@@ -166,6 +166,7 @@ The default settings don't conflict, and Prettier plugins can quickly fix up ord
166
166
 
167
167
  | Name                         | Description | 💼 | 🔧 | 💡 | ❌ |
168
168
  | :------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------- | :--- | :- | :- | :- |
169
+ | [exports-subpaths-style](docs/rules/exports-subpaths-style.md) | Enforce consistent format for the exports field (implicit or explicit subpaths). | | 🔧 | | |
169
170
  | [no-empty-fields](docs/rules/no-empty-fields.md) | Reports on unnecessary empty arrays and objects. | ✔️ ✅ | | 💡 | |
170
171
  | [no-redundant-files](docs/rules/no-redundant-files.md) | Prevents adding unnecessary / redundant files. | ✔️ ✅ | | 💡 | |
171
172
  | [order-properties](docs/rules/order-properties.md) | Package properties must be declared in standard order | ✔️ ✅ | 🔧 | | |
package/lib/plugin.js CHANGED
@@ -1,41 +1,43 @@
1
- import { rule } from "./rules/no-empty-fields.js";
2
- import { rule as rule$1 } from "./rules/no-redundant-files.js";
3
- import { rule as rule$2 } from "./rules/order-properties.js";
4
- import { rule as rule$3 } from "./rules/repository-shorthand.js";
1
+ import { rule } from "./rules/exports-subpaths-style.js";
2
+ import { rule as rule$1 } from "./rules/no-empty-fields.js";
3
+ import { rule as rule$2 } from "./rules/no-redundant-files.js";
4
+ import { rule as rule$3 } from "./rules/order-properties.js";
5
+ import { rule as rule$4 } from "./rules/repository-shorthand.js";
5
6
  import { rules } from "./rules/require-properties.js";
6
- import { rule as rule$4 } from "./rules/restrict-dependency-ranges.js";
7
- import { rule as rule$5 } from "./rules/sort-collections.js";
8
- import { rule as rule$6 } from "./rules/unique-dependencies.js";
9
- import { rule as rule$7 } from "./rules/valid-bin.js";
10
- import { rule as rule$8 } from "./rules/valid-local-dependency.js";
11
- import { rule as rule$9 } from "./rules/valid-name.js";
12
- import { rule as rule$10 } from "./rules/valid-package-definition.js";
7
+ import { rule as rule$5 } from "./rules/restrict-dependency-ranges.js";
8
+ import { rule as rule$6 } from "./rules/sort-collections.js";
9
+ import { rule as rule$7 } from "./rules/unique-dependencies.js";
10
+ import { rule as rule$8 } from "./rules/valid-bin.js";
11
+ import { rule as rule$9 } from "./rules/valid-local-dependency.js";
12
+ import { rule as rule$10 } from "./rules/valid-name.js";
13
+ import { rule as rule$11 } from "./rules/valid-package-definition.js";
13
14
  import { rules as rules$1 } from "./rules/valid-properties.js";
14
- import { rule as rule$11 } from "./rules/valid-repository-directory.js";
15
- import { rule as rule$12 } from "./rules/valid-version.js";
15
+ import { rule as rule$12 } from "./rules/valid-repository-directory.js";
16
+ import { rule as rule$13 } from "./rules/valid-version.js";
16
17
  import { createRequire } from "node:module";
17
18
  import * as parserJsonc from "jsonc-eslint-parser";
18
19
 
19
20
  //#region src/plugin.ts
20
21
  const { name, version } = createRequire(import.meta.url)("../package.json");
21
22
  const rules$2 = {
22
- "no-empty-fields": rule,
23
- "no-redundant-files": rule$1,
24
- "order-properties": rule$2,
23
+ "exports-subpaths-style": rule,
24
+ "no-empty-fields": rule$1,
25
+ "no-redundant-files": rule$2,
26
+ "order-properties": rule$3,
25
27
  ...rules,
26
- "repository-shorthand": rule$3,
27
- "restrict-dependency-ranges": rule$4,
28
- "sort-collections": rule$5,
29
- "unique-dependencies": rule$6,
28
+ "repository-shorthand": rule$4,
29
+ "restrict-dependency-ranges": rule$5,
30
+ "sort-collections": rule$6,
31
+ "unique-dependencies": rule$7,
30
32
  ...rules$1,
31
- "valid-bin": rule$7,
32
- "valid-local-dependency": rule$8,
33
- "valid-name": rule$9,
34
- "valid-package-definition": rule$10,
35
- "valid-repository-directory": rule$11,
36
- "valid-version": rule$12
33
+ "valid-bin": rule$8,
34
+ "valid-local-dependency": rule$9,
35
+ "valid-name": rule$10,
36
+ "valid-package-definition": rule$11,
37
+ "valid-repository-directory": rule$12,
38
+ "valid-version": rule$13
37
39
  };
38
- const baseRecommendedRules = { ...Object.fromEntries(Object.entries(rules$2).filter(([, rule$13]) => rule$13.meta.docs?.recommended).map(([name$1]) => ["package-json/" + name$1, "error"])) };
40
+ const baseRecommendedRules = { ...Object.fromEntries(Object.entries(rules$2).filter(([, rule$14]) => rule$14.meta.docs?.recommended).map(([name$1]) => ["package-json/" + name$1, "error"])) };
39
41
  const recommendedRules = {
40
42
  ...baseRecommendedRules,
41
43
  "package-json/valid-package-definition": ["error", { ignoreProperties: Object.entries(baseRecommendedRules).filter(([name$1]) => name$1.startsWith("package-json/valid-") && name$1 !== "package-json/valid-package-definition").map(([name$1]) => name$1.replace("package-json/valid-", "")) }]
@@ -0,0 +1,18 @@
1
+ import { PackageJsonRuleModule } from "../createRule.js";
2
+
3
+ //#region src/rules/exports-subpaths-style.d.ts
4
+ declare const rule: PackageJsonRuleModule<[({
5
+ prefer?: "explicit" | "implicit" | undefined;
6
+ } | undefined)?], [{
7
+ readonly additionalProperties: false;
8
+ readonly properties: {
9
+ readonly prefer: {
10
+ readonly description: "Specifies which exports format to enforce.";
11
+ readonly enum: readonly ["implicit", "explicit"];
12
+ readonly type: "string";
13
+ };
14
+ };
15
+ readonly type: "object";
16
+ }]>;
17
+ //#endregion
18
+ export { rule };
@@ -0,0 +1,73 @@
1
+ import { createRule } from "../createRule.js";
2
+ import { isJSONStringLiteral } from "../utils/predicates.js";
3
+
4
+ //#region src/rules/exports-subpaths-style.ts
5
+ function isImplicitFormat(node) {
6
+ if (node.type === "JSONLiteral") return true;
7
+ return node.properties.every((property) => !isJSONStringLiteral(property.key) || !property.key.value.startsWith("."));
8
+ }
9
+ const rule = createRule({
10
+ create(context) {
11
+ const [{ prefer = "explicit" } = {}] = context.options;
12
+ function validateForExplicit(node) {
13
+ const { value } = node;
14
+ if (value.type !== "JSONLiteral" && value.type !== "JSONObjectExpression" || !isImplicitFormat(value)) return;
15
+ context.report({
16
+ fix(fixer) {
17
+ const valueText = context.sourceCode.getText(value);
18
+ const fixedValue = JSON.stringify({ ".": JSON.parse(valueText) }, null, 2);
19
+ return fixer.replaceText(value, fixedValue);
20
+ },
21
+ messageId: "preferExplicit",
22
+ node: value
23
+ });
24
+ }
25
+ function validateForImplicit(node) {
26
+ const { value } = node;
27
+ if (value.type !== "JSONObjectExpression") return;
28
+ if (value.properties.length !== 1 || !isJSONStringLiteral(value.properties[0].key) || value.properties[0].key.value !== ".") return;
29
+ const dotProperty = value.properties[0];
30
+ context.report({
31
+ fix(fixer) {
32
+ const valueText = context.sourceCode.getText(dotProperty.value);
33
+ const fixedValue = JSON.stringify(JSON.parse(valueText), null, 2);
34
+ return fixer.replaceText(value, fixedValue);
35
+ },
36
+ messageId: "preferImplicit",
37
+ node: value
38
+ });
39
+ }
40
+ return { JSONProperty(node) {
41
+ if (node.key.type !== "JSONLiteral" || node.key.value !== "exports" || node.parent.parent.parent.type !== "Program") return;
42
+ if (prefer === "explicit") validateForExplicit(node);
43
+ else validateForImplicit(node);
44
+ } };
45
+ },
46
+ meta: {
47
+ defaultOptions: [{ prefer: "explicit" }],
48
+ docs: {
49
+ category: "Best Practices",
50
+ description: "Enforce consistent format for the exports field (implicit or explicit subpaths).",
51
+ recommended: false
52
+ },
53
+ fixable: "code",
54
+ messages: {
55
+ preferExplicit: "Prefer explicit subpaths format with \".\" key for single root export.",
56
+ preferImplicit: "Prefer implicit format without \".\" key for single root export."
57
+ },
58
+ schema: [{
59
+ additionalProperties: false,
60
+ properties: { prefer: {
61
+ description: "Specifies which exports format to enforce.",
62
+ enum: ["implicit", "explicit"],
63
+ type: "string"
64
+ } },
65
+ type: "object"
66
+ }],
67
+ type: "suggestion"
68
+ },
69
+ name: "exports-subpaths-style"
70
+ });
71
+
72
+ //#endregion
73
+ export { rule };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-package-json",
3
- "version": "0.58.0",
3
+ "version": "0.59.0",
4
4
  "description": "Rules for consistent, readable, and valid package.json files. 🗂️",
5
5
  "homepage": "https://github.com/JoshuaKGoldberg/eslint-plugin-package-json#readme",
6
6
  "bugs": {
@@ -56,7 +56,7 @@
56
56
  },
57
57
  "devDependencies": {
58
58
  "@eslint-community/eslint-plugin-eslint-comments": "4.5.0",
59
- "@eslint/js": "9.37.0",
59
+ "@eslint/js": "9.38.0",
60
60
  "@release-it/conventional-changelog": "10.0.0",
61
61
  "@types/estree": "1.0.7",
62
62
  "@types/node": "22.18.0",
@@ -65,7 +65,7 @@
65
65
  "@vitest/coverage-v8": "3.2.0",
66
66
  "@vitest/eslint-plugin": "1.3.3",
67
67
  "console-fail-test": "0.5.0",
68
- "eslint": "9.37.0",
68
+ "eslint": "9.38.0",
69
69
  "eslint-doc-generator": "2.3.0",
70
70
  "eslint-plugin-eslint-plugin": "7.0.0",
71
71
  "eslint-plugin-jsdoc": "61.1.0",
@@ -78,7 +78,7 @@
78
78
  "jiti": "2.6.0",
79
79
  "json-schema-to-ts": "3.1.1",
80
80
  "jsonc-eslint-parser": "2.4.1",
81
- "knip": "5.65.0",
81
+ "knip": "5.66.0",
82
82
  "lint-staged": "16.2.0",
83
83
  "markdownlint": "0.39.0",
84
84
  "markdownlint-cli": "0.45.0",