eslint-plugin-package-json 1.9.0 โ†’ 1.10.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/README.md CHANGED
@@ -15,7 +15,7 @@
15
15
  <p align="center">
16
16
  <!-- prettier-ignore-start -->
17
17
  <!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
18
- <a href="#contributors" target="_blank"><img alt="๐Ÿ‘ช All Contributors: 44" src="https://img.shields.io/badge/%F0%9F%91%AA_all_contributors-44-21bb42.svg" /></a>
18
+ <a href="#contributors" target="_blank"><img alt="๐Ÿ‘ช All Contributors: 45" src="https://img.shields.io/badge/%F0%9F%91%AA_all_contributors-45-21bb42.svg" /></a>
19
19
  <!-- ALL-CONTRIBUTORS-BADGE:END -->
20
20
  <!-- prettier-ignore-end -->
21
21
  <a href="https://codecov.io/gh/michaelfaith/eslint-plugin-package-json" target="_blank"><img alt="๐Ÿงช Coverage" src="https://img.shields.io/codecov/c/github/michaelfaith/eslint-plugin-package-json?label=%F0%9F%A7%AA%20coverage" /></a>
@@ -49,6 +49,7 @@ See [Getting Started](https://eslint-plugin-package-json.dev/getting-started) fo
49
49
  | [prefer-rolling-workspace-spec](https://eslint-plugin-package-json.dev/rules/prefer-rolling-workspace-spec) | Require that dependencies declared with workspace protocol use a rolling workspace spec instead of a specific semver range. | | | ๐Ÿ’ก |
50
50
  | [repository-shorthand](https://eslint-plugin-package-json.dev/rules/repository-shorthand) | Enforce either object or shorthand declaration for repository. | โœ… | ๐Ÿ”ง | |
51
51
  | [require-attribution](https://eslint-plugin-package-json.dev/rules/require-attribution) | Ensures that proper attribution is included, requiring that either `author` or `contributors` is defined, and that if `contributors` is present, it should include at least one contributor. | โœ… | | ๐Ÿ’ก |
52
+ | [require-package-json-export](https://eslint-plugin-package-json.dev/rules/require-package-json-export) | Ensures that if a package specifies `exports` (or `publishConfig.exports`), an explicit `package.json` export is included. | | | ๐Ÿ’ก |
52
53
  | [restrict-dependency-ranges](https://eslint-plugin-package-json.dev/rules/restrict-dependency-ranges) | Restricts the range of dependencies to allow or disallow specific types of ranges. | | | ๐Ÿ’ก |
53
54
  | [restrict-dist-tags](https://eslint-plugin-package-json.dev/rules/restrict-dist-tags) | Restricts dependency dist-tags to an allowed list and selected dependency types. | | | |
54
55
  | [restrict-private-properties](https://eslint-plugin-package-json.dev/rules/restrict-private-properties) | Disallows unnecessary properties in private packages. | | ๐Ÿ”ง | ๐Ÿ’ก |
@@ -238,13 +239,14 @@ Thanks! ๐Ÿป
238
239
  <tr>
239
240
  <td align="center" valign="top" width="14.28%"><a href="https://github.com/sirugh"><img src="https://avatars.githubusercontent.com/u/1278869?v=4?s=100" width="100px;" alt="Stephen"/><br /><sub><b>Stephen</b></sub></a><br /><a href="https://github.com/michaelfaith/eslint-plugin-package-json/commits?author=sirugh" title="Code">๐Ÿ’ป</a></td>
240
241
  <td align="center" valign="top" width="14.28%"><a href="https://hyoban.cc"><img src="https://avatars.githubusercontent.com/u/38493346?v=4?s=100" width="100px;" alt="Stephen Zhou"/><br /><sub><b>Stephen Zhou</b></sub></a><br /><a href="https://github.com/michaelfaith/eslint-plugin-package-json/issues?q=author%3Ahyoban" title="Bug reports">๐Ÿ›</a> <a href="https://github.com/michaelfaith/eslint-plugin-package-json/commits?author=hyoban" title="Code">๐Ÿ’ป</a> <a href="#ideas-hyoban" title="Ideas, Planning, & Feedback">๐Ÿค”</a> <a href="https://github.com/michaelfaith/eslint-plugin-package-json/commits?author=hyoban" title="Documentation">๐Ÿ“–</a></td>
242
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/thernstig"><img src="https://avatars.githubusercontent.com/u/30827238?v=4?s=100" width="100px;" alt="Tobias Hernstig"/><br /><sub><b>Tobias Hernstig</b></sub></a><br /><a href="https://github.com/michaelfaith/eslint-plugin-package-json/issues?q=author%3Athernstig" title="Bug reports">๐Ÿ›</a></td>
241
243
  <td align="center" valign="top" width="14.28%"><a href="https://www.wesleytodd.com/"><img src="https://avatars.githubusercontent.com/u/1027776?v=4?s=100" width="100px;" alt="Wes Todd"/><br /><sub><b>Wes Todd</b></sub></a><br /><a href="#ideas-wesleytodd" title="Ideas, Planning, & Feedback">๐Ÿค”</a></td>
242
244
  <td align="center" valign="top" width="14.28%"><a href="https://ota-meshi.github.io/"><img src="https://avatars.githubusercontent.com/u/16508807?v=4?s=100" width="100px;" alt="Yosuke Ota"/><br /><sub><b>Yosuke Ota</b></sub></a><br /><a href="https://github.com/michaelfaith/eslint-plugin-package-json/issues?q=author%3Aota-meshi" title="Bug reports">๐Ÿ›</a> <a href="https://github.com/michaelfaith/eslint-plugin-package-json/commits?author=ota-meshi" title="Code">๐Ÿ’ป</a></td>
243
245
  <td align="center" valign="top" width="14.28%"><a href="https://github.com/b3rnhard"><img src="https://avatars.githubusercontent.com/u/10774404?v=4?s=100" width="100px;" alt="b3rnhard"/><br /><sub><b>b3rnhard</b></sub></a><br /><a href="https://github.com/michaelfaith/eslint-plugin-package-json/issues?q=author%3Ab3rnhard" title="Bug reports">๐Ÿ›</a></td>
244
246
  <td align="center" valign="top" width="14.28%"><a href="https://github.com/chouchouji"><img src="https://avatars.githubusercontent.com/u/70570907?v=4?s=100" width="100px;" alt="chouchouji"/><br /><sub><b>chouchouji</b></sub></a><br /><a href="https://github.com/michaelfaith/eslint-plugin-package-json/commits?author=chouchouji" title="Code">๐Ÿ’ป</a></td>
245
- <td align="center" valign="top" width="14.28%"><a href="https://github.com/michaelfaith"><img src="https://avatars.githubusercontent.com/u/8071845?v=4?s=100" width="100px;" alt="michael faith"/><br /><sub><b>michael faith</b></sub></a><br /><a href="#infra-michaelfaith" title="Infrastructure (Hosting, Build-Tools, etc)">๐Ÿš‡</a> <a href="https://github.com/michaelfaith/eslint-plugin-package-json/commits?author=michaelfaith" title="Code">๐Ÿ’ป</a> <a href="#maintenance-michaelfaith" title="Maintenance">๐Ÿšง</a> <a href="#ideas-michaelfaith" title="Ideas, Planning, & Feedback">๐Ÿค”</a> <a href="https://github.com/michaelfaith/eslint-plugin-package-json/issues?q=author%3Amichaelfaith" title="Bug reports">๐Ÿ›</a> <a href="#tool-michaelfaith" title="Tools">๐Ÿ”ง</a> <a href="https://github.com/michaelfaith/eslint-plugin-package-json/commits?author=michaelfaith" title="Documentation">๐Ÿ“–</a> <a href="#projectManagement-michaelfaith" title="Project Management">๐Ÿ“†</a></td>
246
247
  </tr>
247
248
  <tr>
249
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/michaelfaith"><img src="https://avatars.githubusercontent.com/u/8071845?v=4?s=100" width="100px;" alt="michael faith"/><br /><sub><b>michael faith</b></sub></a><br /><a href="#infra-michaelfaith" title="Infrastructure (Hosting, Build-Tools, etc)">๐Ÿš‡</a> <a href="https://github.com/michaelfaith/eslint-plugin-package-json/commits?author=michaelfaith" title="Code">๐Ÿ’ป</a> <a href="#maintenance-michaelfaith" title="Maintenance">๐Ÿšง</a> <a href="#ideas-michaelfaith" title="Ideas, Planning, & Feedback">๐Ÿค”</a> <a href="https://github.com/michaelfaith/eslint-plugin-package-json/issues?q=author%3Amichaelfaith" title="Bug reports">๐Ÿ›</a> <a href="#tool-michaelfaith" title="Tools">๐Ÿ”ง</a> <a href="https://github.com/michaelfaith/eslint-plugin-package-json/commits?author=michaelfaith" title="Documentation">๐Ÿ“–</a> <a href="#projectManagement-michaelfaith" title="Project Management">๐Ÿ“†</a></td>
248
250
  <td align="center" valign="top" width="14.28%"><a href="https://roottool.vercel.app"><img src="https://avatars.githubusercontent.com/u/11808736?v=4?s=100" width="100px;" alt="roottool"/><br /><sub><b>roottool</b></sub></a><br /><a href="https://github.com/michaelfaith/eslint-plugin-package-json/commits?author=roottool" title="Code">๐Ÿ’ป</a></td>
249
251
  <td align="center" valign="top" width="14.28%"><a href="https://github.com/sunnytsang1998"><img src="https://avatars.githubusercontent.com/u/207208443?v=4?s=100" width="100px;" alt="sunnytsang1998"/><br /><sub><b>sunnytsang1998</b></sub></a><br /><a href="https://github.com/michaelfaith/eslint-plugin-package-json/issues?q=author%3Asunnytsang1998" title="Bug reports">๐Ÿ›</a></td>
250
252
  </tr>
@@ -1,4 +1,4 @@
1
- import { t as plugin$1 } from "../plugin-2AyGRwRO.mjs";
1
+ import { t as plugin$1 } from "../plugin-BfdzXm-J.mjs";
2
2
  import { toCompatRule } from "eslint-json-compat-utils";
3
3
  //#region src/experimental/plugin.ts
4
4
  const language = "json/json";
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as plugin } from "./plugin-2AyGRwRO.mjs";
1
+ import { t as plugin } from "./plugin-BfdzXm-J.mjs";
2
2
  //#region src/index.ts
3
3
  const rules = plugin.rules;
4
4
  const configs = plugin.configs;
@@ -41,8 +41,7 @@ function createRule(rule) {
41
41
  const ruleGroup = rule.meta.docs?.ruleGroup;
42
42
  return {
43
43
  create(context) {
44
- if (!isPackageJson(context.filename)) return {};
45
- return rule.create(context);
44
+ return isPackageJson(context.filename) ? rule.create(context) : {};
46
45
  },
47
46
  meta: {
48
47
  ...rule.meta,
@@ -55,7 +54,7 @@ function createRule(rule) {
55
54
  }
56
55
  //#endregion
57
56
  //#region src/rules/bin-name-casing.ts
58
- const rule$20 = createRule({
57
+ const rule$21 = createRule({
59
58
  create(context) {
60
59
  return { "Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.value=bin]"(node) {
61
60
  if (node.value.type === "JSONObjectExpression") for (const property of node.value.properties) {
@@ -93,17 +92,16 @@ const rule$20 = createRule({
93
92
  });
94
93
  //#endregion
95
94
  //#region src/rules/exports-subpaths-style.ts
96
- function isImplicitFormat(node) {
97
- if (node.type === "JSONLiteral") return true;
98
- return node.properties.every((property) => !isJSONStringLiteral(property.key) || !property.key.value.startsWith("."));
95
+ function isImplicitFormat$1(node) {
96
+ return node.type === "JSONLiteral" ? true : node.properties.every((property) => !isJSONStringLiteral(property.key) || !property.key.value.startsWith("."));
99
97
  }
100
- const rule$19 = createRule({
98
+ const rule$20 = createRule({
101
99
  create(context) {
102
100
  const [{ prefer = "explicit" } = {}] = context.options;
103
101
  function validateForExplicit(node) {
104
102
  const { value } = node;
105
103
  if (isJSONNullLiteral(value)) return;
106
- if (value.type !== "JSONLiteral" && value.type !== "JSONObjectExpression" || !isImplicitFormat(value)) return;
104
+ if (value.type !== "JSONLiteral" && value.type !== "JSONObjectExpression" || !isImplicitFormat$1(value)) return;
107
105
  context.report({
108
106
  fix(fixer) {
109
107
  const valueText = context.sourceCode.getText(value);
@@ -200,25 +198,21 @@ const getTopLevelProperty = (node) => {
200
198
  return n.type === "JSONProperty" ? n.key : void 0;
201
199
  };
202
200
  const defaultIgnoreProperties = ["files"];
203
- const rule$18 = createRule({
201
+ const rule$19 = createRule({
204
202
  create(context) {
205
203
  const ignoreProperties = new Set(context.options[0]?.ignoreProperties ?? defaultIgnoreProperties);
206
204
  return {
207
205
  JSONArrayExpression(node) {
208
206
  const topLevelProperty = getTopLevelProperty(node);
209
- if (!topLevelProperty) return;
210
- if (!node.elements.length) {
211
- const topLevelPropertyName = topLevelProperty.value;
212
- if (!ignoreProperties.has(topLevelPropertyName)) report(context, getNode(node));
213
- }
207
+ if (!topLevelProperty || node.elements.length) return;
208
+ const topLevelPropertyName = topLevelProperty.value;
209
+ if (!ignoreProperties.has(topLevelPropertyName)) report(context, getNode(node));
214
210
  },
215
211
  JSONObjectExpression(node) {
216
212
  const topLevelProperty = getTopLevelProperty(node);
217
- if (!topLevelProperty) return;
218
- if (!node.properties.length) {
219
- const topLevelPropertyName = topLevelProperty.value;
220
- if (!ignoreProperties.has(topLevelPropertyName)) report(context, getNode(node));
221
- }
213
+ if (!topLevelProperty || node.properties.length) return;
214
+ const topLevelPropertyName = topLevelProperty.value;
215
+ if (!ignoreProperties.has(topLevelPropertyName)) report(context, getNode(node));
222
216
  }
223
217
  };
224
218
  },
@@ -256,7 +250,7 @@ const getBundledDependencyNames = (value) => {
256
250
  if (value?.type === "JSONArrayExpression") for (const element of value.elements.filter(isNotNullish).filter(isJSONStringLiteral)) names.add(element.value);
257
251
  return names;
258
252
  };
259
- const rule$17 = createRule({
253
+ const rule$18 = createRule({
260
254
  create(context) {
261
255
  const ignorePrivate = context.options[0]?.ignorePrivate ?? true;
262
256
  let isPrivate = false;
@@ -327,7 +321,7 @@ const getCachedLocalFileRegex = (filename) => {
327
321
  return regex;
328
322
  }
329
323
  };
330
- const rule$16 = createRule({
324
+ const rule$17 = createRule({
331
325
  create(context) {
332
326
  const entryCache = {
333
327
  bin: [],
@@ -404,7 +398,7 @@ const rule$16 = createRule({
404
398
  });
405
399
  //#endregion
406
400
  //#region src/rules/no-redundant-publishConfig.ts
407
- const rule$15 = createRule({
401
+ const rule$16 = createRule({
408
402
  create(context) {
409
403
  let packageName;
410
404
  let publishConfigAccessProperty;
@@ -450,7 +444,7 @@ const rule$15 = createRule({
450
444
  });
451
445
  //#endregion
452
446
  //#region src/rules/order-properties.ts
453
- const rule$14 = createRule({
447
+ const rule$15 = createRule({
454
448
  create(context) {
455
449
  return { "Program:exit"() {
456
450
  const { ast, text } = context.sourceCode;
@@ -510,7 +504,7 @@ const rule$14 = createRule({
510
504
  //#region src/rules/prefer-rolling-workspace-spec.ts
511
505
  const rollingWorkspaceSpecRegex = /^workspace:[*^~]?$/;
512
506
  const convertibleSpecRegex = /^(workspace:[~^])/;
513
- const rule$13 = createRule({
507
+ const rule$14 = createRule({
514
508
  create(context) {
515
509
  const { ignoreDependencies = [], ignorePatterns = [] } = context.options[0] ?? {};
516
510
  const ignoreRegexes = ignorePatterns.map((pattern) => new RegExp(pattern));
@@ -608,14 +602,13 @@ const createUrl = (shorthand) => {
608
602
  }
609
603
  return `${providerUrls.github}${shorthand}`;
610
604
  };
611
- const rule$12 = createRule({
605
+ const rule$13 = createRule({
612
606
  create(context) {
613
607
  const [{ form = "object" } = {}] = context.options;
614
608
  function validateRepositoryForObject(node) {
615
609
  if (isJSONStringLiteral(node.value)) context.report({
616
610
  fix(fixer) {
617
- if (!isJSONStringLiteral(node.value) || node.value.value.split("/").filter(Boolean).length !== 2) return null;
618
- return fixer.replaceText(node.value, JSON.stringify({
611
+ return !isJSONStringLiteral(node.value) || node.value.value.split("/").filter(Boolean).length !== 2 ? null : fixer.replaceText(node.value, JSON.stringify({
619
612
  type: "git",
620
613
  url: createUrl(node.value.value)
621
614
  }, null, 2));
@@ -687,7 +680,7 @@ const rule$12 = createRule({
687
680
  });
688
681
  //#endregion
689
682
  //#region src/rules/require-attribution.ts
690
- const rule$11 = createRule({
683
+ const rule$12 = createRule({
691
684
  create(context) {
692
685
  const preferContributorsOnly = context.options[0]?.preferContributorsOnly ?? false;
693
686
  const ignorePrivate = context.options[0]?.ignorePrivate ?? true;
@@ -774,6 +767,86 @@ const rule$11 = createRule({
774
767
  name: "require-attribution"
775
768
  });
776
769
  //#endregion
770
+ //#region src/utils/increaseIndent.ts
771
+ const increaseIndent = (original, additional) => original.split("\n").join(`\n${additional}`);
772
+ //#endregion
773
+ //#region src/rules/require-package-json-export.ts
774
+ function isImplicitFormat(node) {
775
+ return node.properties.every((property) => !isJSONStringLiteral(property.key) || !property.key.value.startsWith("."));
776
+ }
777
+ const rule$11 = createRule({
778
+ create(context) {
779
+ let publishConfigExportsValueNode;
780
+ let exportsValueNode;
781
+ return {
782
+ "Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.value=exports]"(node) {
783
+ if (node.value.type === "JSONObjectExpression" || isJSONStringLiteral(node.value)) exportsValueNode = node.value;
784
+ },
785
+ "Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.value=publishConfig] > JSONObjectExpression > JSONProperty[key.value=exports]"(node) {
786
+ if (node.value.type === "JSONObjectExpression" || isJSONStringLiteral(node.value)) publishConfigExportsValueNode = node.value;
787
+ },
788
+ "Program:exit"() {
789
+ const exportsNode = publishConfigExportsValueNode ?? exportsValueNode;
790
+ if (!exportsNode) return;
791
+ const isPublishConfig = !!publishConfigExportsValueNode;
792
+ const { text } = context.sourceCode;
793
+ const { indent, type } = detectIndent(text);
794
+ const indentUnit = type === "tab" ? " " : indent || " ";
795
+ const extraIndent = isPublishConfig ? `${indentUnit}${indentUnit}` : indentUnit;
796
+ if (isJSONStringLiteral(exportsNode) || isImplicitFormat(exportsNode)) {
797
+ context.report({
798
+ messageId: "missing",
799
+ node: exportsNode,
800
+ suggest: [{
801
+ messageId: "addExport",
802
+ fix(fixer) {
803
+ const valueText = context.sourceCode.getText(exportsNode);
804
+ const fixedValue = increaseIndent(JSON.stringify({
805
+ ".": JSON.parse(valueText),
806
+ "./package.json": "./package.json"
807
+ }, null, indentUnit), extraIndent);
808
+ return fixer.replaceText(exportsNode, fixedValue);
809
+ }
810
+ }]
811
+ });
812
+ return;
813
+ }
814
+ if (exportsNode.properties.some((property) => isJSONStringLiteral(property.key) && property.key.value === "./package.json")) return;
815
+ context.report({
816
+ messageId: "missing",
817
+ node: exportsNode,
818
+ suggest: [{
819
+ messageId: "addExport",
820
+ fix(fixer) {
821
+ const valueText = context.sourceCode.getText(exportsNode);
822
+ const existingExports = JSON.parse(valueText);
823
+ const fixedValue = increaseIndent(JSON.stringify({
824
+ ...existingExports,
825
+ "./package.json": "./package.json"
826
+ }, null, indentUnit), extraIndent);
827
+ return fixer.replaceText(exportsNode, fixedValue);
828
+ }
829
+ }]
830
+ });
831
+ }
832
+ };
833
+ },
834
+ meta: {
835
+ docs: {
836
+ category: "Best Practices",
837
+ description: "Ensures that if a package specifies `exports` (or `publishConfig.exports`), an explicit `package.json` export is included."
838
+ },
839
+ hasSuggestions: true,
840
+ messages: {
841
+ missing: "An export for the package.json is required, and missing.",
842
+ addExport: "Add package.json export."
843
+ },
844
+ schema: [],
845
+ type: "suggestion"
846
+ },
847
+ name: "require-package-json-export"
848
+ });
849
+ //#endregion
777
850
  //#region src/utils/createSimpleRequirePropertyRule.ts
778
851
  /**
779
852
  * Given a top-level property name, create a rule that requires that property to be present.
@@ -790,21 +863,20 @@ const createSimpleRequirePropertyRule = (propertyName, { category, fixValue, ign
790
863
  const ignorePrivate = context.options[0]?.ignorePrivate ?? (typeof enforceForPrivate === "boolean" ? !enforceForPrivate : ignorePrivateDefault);
791
864
  return { "Program > JSONExpressionStatement > JSONObjectExpression"(node) {
792
865
  if (ignorePrivate && node.properties.some((property) => isJSONStringLiteral(property.key) && property.key.value === "private" && property.value.type === "JSONLiteral" && property.value.value === true)) return;
793
- if (node.properties.every((property) => !(isJSONStringLiteral(property.key) && property.key.value === propertyName))) {
794
- const resolvedValue = typeof fixValue === "function" ? fixValue() : fixValue;
795
- context.report({
796
- data: { property: propertyName },
797
- fix: resolvedValue === void 0 ? void 0 : function* (fixer) {
798
- yield fixer.insertTextAfterRange([0, 1], `\n "${propertyName}": ${JSON.stringify(resolvedValue, null, 2).split("\n").join("\n ")}`);
799
- yield node.properties.length > 0 ? fixer.insertTextAfterRange([0, 1], ",") : fixer.insertTextAfterRange([0, 1], "\n");
800
- },
801
- loc: {
802
- column: 0,
803
- line: 1
804
- },
805
- messageId: "missing"
806
- });
807
- }
866
+ if (node.properties.some((property) => isJSONStringLiteral(property.key) && property.key.value === propertyName)) return;
867
+ const resolvedValue = typeof fixValue === "function" ? fixValue() : fixValue;
868
+ context.report({
869
+ data: { property: propertyName },
870
+ fix: resolvedValue === void 0 ? void 0 : function* (fixer) {
871
+ yield fixer.insertTextAfterRange([0, 1], `\n "${propertyName}": ${JSON.stringify(resolvedValue, null, 2).split("\n").join("\n ")}`);
872
+ yield node.properties.length > 0 ? fixer.insertTextAfterRange([0, 1], ",") : fixer.insertTextAfterRange([0, 1], "\n");
873
+ },
874
+ loc: {
875
+ column: 0,
876
+ line: 1
877
+ },
878
+ messageId: "missing"
879
+ });
808
880
  } };
809
881
  },
810
882
  meta: {
@@ -1001,7 +1073,7 @@ const schemaOptions = {
1001
1073
  const normalizeRangeType = (rangeTypeOrSymbol) => RANGE_TYPES.find((rangeType) => "symbol" in rangeType && rangeType.symbol === rangeTypeOrSymbol || rangeType.alias === rangeTypeOrSymbol);
1002
1074
  /** @returns `undefined` if workspace versions are not supported for the specified {@link rangeType} */
1003
1075
  const getWorkspaceVersionForRange = (rangeType) => {
1004
- if ("workspaceSymbol" in rangeType) return `workspace:${rangeType.workspaceSymbol}`;
1076
+ return "workspaceSymbol" in rangeType ? `workspace:${rangeType.workspaceSymbol}` : void 0;
1005
1077
  };
1006
1078
  /** For displaying a range type in a user-facing way (ie. an error message). */
1007
1079
  const displayRangeType = (rangeType) => "symbol" in rangeType ? rangeType.symbol : rangeType.alias;
@@ -1392,8 +1464,7 @@ const rule$5 = createRule({
1392
1464
  const bKey = b.key.value;
1393
1465
  const ai = rank(aKey);
1394
1466
  const bi = rank(bKey);
1395
- if (ai !== bi) return ai - bi;
1396
- return naturalCompare(aKey, bKey);
1467
+ return ai === bi ? naturalCompare(aKey, bKey) : ai - bi;
1397
1468
  });
1398
1469
  if (currentOrder.some((property, i) => desiredOrder[i] !== property)) context.report({
1399
1470
  data: { key },
@@ -1557,10 +1628,9 @@ const rule$3 = createRule({
1557
1628
  if (!dependencyPropertyNames.has(node.key.value)) return;
1558
1629
  const nodeValueType = node.value.type;
1559
1630
  if (nodeValueType === "JSONArrayExpression") check(node.value.elements, (element) => element);
1560
- if (nodeValueType === "JSONObjectExpression") {
1561
- check(node.value.properties.map((property) => property.key), (property) => property.parent);
1562
- if (trackForCrossGroupUniqueness.includes(node.key.value)) dependenciesCache[node.key.value] = node.value.properties;
1563
- }
1631
+ if (nodeValueType !== "JSONObjectExpression") return;
1632
+ check(node.value.properties.map((property) => property.key), (property) => property.parent);
1633
+ if (trackForCrossGroupUniqueness.includes(node.key.value)) dependenciesCache[node.key.value] = node.value.properties;
1564
1634
  },
1565
1635
  "Program:exit"() {
1566
1636
  const dependencyNames = new Set(dependenciesCache.dependencies.map((node) => node.key).filter(isJSONStringLiteral).map((dependencyNameNode) => dependencyNameNode.value));
@@ -1810,8 +1880,7 @@ function* lookup(cwd = process.cwd()) {
1810
1880
  }
1811
1881
  }
1812
1882
  function parsePackageJson(filepath) {
1813
- if (!filepath || !fs.existsSync(filepath)) return null;
1814
- return handlePackageManager(filepath);
1883
+ return !filepath || !fs.existsSync(filepath) ? null : handlePackageManager(filepath);
1815
1884
  }
1816
1885
  /**
1817
1886
  * Detects the package manager used in the project.
@@ -1855,10 +1924,10 @@ function getNameAndVersion(pkg) {
1855
1924
  version: normalizeVersion(version)
1856
1925
  };
1857
1926
  }
1858
- if (typeof pkg.devEngines?.packageManager?.name === "string") return {
1927
+ return typeof pkg.devEngines?.packageManager?.name === "string" ? {
1859
1928
  name: pkg.devEngines.packageManager.name,
1860
1929
  version: normalizeVersion(pkg.devEngines.packageManager.version)
1861
- };
1930
+ } : void 0;
1862
1931
  }
1863
1932
  function handlePackageManager(filepath) {
1864
1933
  try {
@@ -2007,17 +2076,18 @@ const rule = createRule({
2007
2076
  //#region src/plugin.ts
2008
2077
  const require = createRequire(import.meta.url);
2009
2078
  const rules = {
2010
- "bin-name-casing": rule$20,
2011
- "exports-subpaths-style": rule$19,
2012
- "no-empty-fields": rule$18,
2013
- "no-local-dependencies": rule$17,
2014
- "no-redundant-files": rule$16,
2015
- "no-redundant-publishConfig": rule$15,
2016
- "order-properties": rule$14,
2017
- "prefer-rolling-workspace-spec": rule$13,
2018
- "require-attribution": rule$11,
2079
+ "bin-name-casing": rule$21,
2080
+ "exports-subpaths-style": rule$20,
2081
+ "no-empty-fields": rule$19,
2082
+ "no-local-dependencies": rule$18,
2083
+ "no-redundant-files": rule$17,
2084
+ "no-redundant-publishConfig": rule$16,
2085
+ "order-properties": rule$15,
2086
+ "prefer-rolling-workspace-spec": rule$14,
2087
+ "require-attribution": rule$12,
2088
+ "require-package-json-export": rule$11,
2019
2089
  ...rules$2,
2020
- "repository-shorthand": rule$12,
2090
+ "repository-shorthand": rule$13,
2021
2091
  "restrict-dependency-ranges": rule$10,
2022
2092
  "restrict-dist-tags": rule$9,
2023
2093
  "restrict-private-properties": rule$8,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-package-json",
3
- "version": "1.9.0",
3
+ "version": "1.10.0",
4
4
  "description": "Rules for consistent, readable, and valid package.json files. ๐Ÿ—‚๏ธ",
5
5
  "homepage": "https://github.com/michaelfaith/eslint-plugin-package-json#readme",
6
6
  "bugs": {
@@ -36,7 +36,8 @@
36
36
  "./experimental": {
37
37
  "types": "./dist/experimental/index.d.mts",
38
38
  "default": "./dist/experimental/index.mjs"
39
- }
39
+ },
40
+ "./package.json": "./package.json"
40
41
  },
41
42
  "main": "dist/index.mjs",
42
43
  "files": [
@@ -59,38 +60,38 @@
59
60
  "devDependencies": {
60
61
  "@astrojs/check": "0.9.10",
61
62
  "@astrojs/markdown-satteri": "0.4.1",
62
- "@astrojs/starlight": "0.42.0",
63
+ "@astrojs/starlight": "0.42.2",
63
64
  "@catppuccin/starlight": "2.1.0",
64
65
  "@eslint-community/eslint-plugin-eslint-comments": "4.8.1",
65
66
  "@eslint/js": "10.0.1",
66
67
  "@eslint/json": "2.1.0",
67
68
  "@eslint/markdown": "8.0.3",
68
69
  "@ianvs/prettier-plugin-sort-imports": "4.7.1",
69
- "@types/node": "24.13.4",
70
+ "@types/node": "24.13.6",
70
71
  "@types/semver": "7.8.0",
71
72
  "@typescript/native": "npm:typescript@7.0.2",
72
73
  "@vitest/coverage-v8": "5.0.1",
73
74
  "@vitest/eslint-plugin": "1.6.27",
74
- "astro": "7.3.2",
75
- "astro-og-canvas": "0.13.1",
75
+ "astro": "7.3.3",
76
+ "astro-og-canvas": "0.13.2",
76
77
  "canvaskit-wasm": "0.42.0",
77
78
  "console-fail-test": "0.6.1",
78
- "eslint": "10.10.0",
79
+ "eslint": "10.11.0",
79
80
  "eslint-doc-generator": "3.7.1",
80
81
  "eslint-plugin-eslint-plugin": "7.6.2",
81
- "eslint-plugin-jsdoc": "64.4.0",
82
+ "eslint-plugin-jsdoc": "64.5.4",
82
83
  "eslint-plugin-jsonc": "3.4.2",
83
84
  "eslint-plugin-n": "18.3.0",
84
85
  "eslint-plugin-node-dependencies": "2.2.0",
85
- "eslint-plugin-perfectionist": "5.11.0",
86
+ "eslint-plugin-perfectionist": "5.11.1",
86
87
  "eslint-plugin-regexp": "3.3.0",
87
- "eslint-plugin-unicorn": "74.0.0",
88
+ "eslint-plugin-unicorn": "75.0.0",
88
89
  "eslint-plugin-yml": "3.8.1",
89
90
  "jiti": "2.7.0",
90
91
  "json-schema-to-ts": "3.1.1",
91
- "knip": "6.35.1",
92
+ "knip": "6.37.0",
92
93
  "pkg-pr-new": "0.0.88",
93
- "prettier": "3.9.6",
94
+ "prettier": "3.9.8",
94
95
  "prettier-plugin-curly": "0.4.1",
95
96
  "prettier-plugin-packagejson": "3.0.2",
96
97
  "prettier-plugin-sh": "0.19.0",