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 +8 -2
- package/README.md +1 -0
- package/lib/plugin.js +29 -27
- package/lib/rules/exports-subpaths-style.d.ts +18 -0
- package/lib/rules/exports-subpaths-style.js +73 -0
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
# [0.
|
|
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
|
-
|
|
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/
|
|
2
|
-
import { rule as rule$1 } from "./rules/no-
|
|
3
|
-
import { rule as rule$2 } from "./rules/
|
|
4
|
-
import { rule as rule$3 } from "./rules/
|
|
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$
|
|
7
|
-
import { rule as rule$
|
|
8
|
-
import { rule as rule$
|
|
9
|
-
import { rule as rule$
|
|
10
|
-
import { rule as rule$
|
|
11
|
-
import { rule as rule$
|
|
12
|
-
import { rule as rule$
|
|
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$
|
|
15
|
-
import { rule as rule$
|
|
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
|
-
"
|
|
23
|
-
"no-
|
|
24
|
-
"
|
|
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$
|
|
27
|
-
"restrict-dependency-ranges": rule$
|
|
28
|
-
"sort-collections": rule$
|
|
29
|
-
"unique-dependencies": rule$
|
|
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$
|
|
32
|
-
"valid-local-dependency": rule$
|
|
33
|
-
"valid-name": rule$
|
|
34
|
-
"valid-package-definition": rule$
|
|
35
|
-
"valid-repository-directory": rule$
|
|
36
|
-
"valid-version": rule$
|
|
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$
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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",
|