eslint-plugin-package-json 1.5.0 → 1.6.1

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.
Files changed (156) hide show
  1. package/README.md +107 -106
  2. package/{lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema.d.mts → dist/createRule-29pC9RoP.d.mts} +49 -4
  3. package/dist/experimental/index.d.mts +68 -0
  4. package/{lib/experimental/plugin.mjs → dist/experimental/index.mjs} +10 -5
  5. package/dist/index.d.mts +99 -0
  6. package/{lib → dist}/index.mjs +1 -1
  7. package/dist/plugin-C-Z8sf5g.mjs +1737 -0
  8. package/package.json +33 -27
  9. package/lib/createRule.d.mts +0 -59
  10. package/lib/createRule.mjs +0 -24
  11. package/lib/experimental/index.d.mts +0 -34
  12. package/lib/experimental/index.mjs +0 -7
  13. package/lib/experimental/plugin.d.mts +0 -40
  14. package/lib/index.d.mts +0 -49
  15. package/lib/node_modules/.pnpm/@types_estree@1.0.8/node_modules/@types/estree/index.d.mts +0 -438
  16. package/lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/definitions/deserializationPattern.d.mts +0 -7
  17. package/lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/definitions/fromSchemaOptions.d.mts +0 -20
  18. package/lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/index.d.mts +0 -11
  19. package/lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/parse-options.d.mts +0 -16
  20. package/lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/parse-schema/allOf.d.mts +0 -13
  21. package/lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/parse-schema/anyOf.d.mts +0 -14
  22. package/lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/parse-schema/array.d.mts +0 -58
  23. package/lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/parse-schema/const.d.mts +0 -14
  24. package/lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/parse-schema/deserialize.d.mts +0 -13
  25. package/lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/parse-schema/enum.d.mts +0 -14
  26. package/lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/parse-schema/ifThenElse.d.mts +0 -20
  27. package/lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/parse-schema/index.d.mts +0 -34
  28. package/lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/parse-schema/multipleTypes.d.mts +0 -14
  29. package/lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/parse-schema/not.d.mts +0 -20
  30. package/lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/parse-schema/nullable.d.mts +0 -14
  31. package/lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/parse-schema/object.d.mts +0 -34
  32. package/lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/parse-schema/oneOf.d.mts +0 -14
  33. package/lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/parse-schema/references/external.d.mts +0 -22
  34. package/lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/parse-schema/references/index.d.mts +0 -13
  35. package/lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/parse-schema/references/internal.d.mts +0 -8
  36. package/lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/parse-schema/references/utils.d.mts +0 -12
  37. package/lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/parse-schema/singleType.d.mts +0 -24
  38. package/lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/parse-schema/utils.d.mts +0 -20
  39. package/lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/type-utils/and.d.mts +0 -4
  40. package/lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/type-utils/extends.d.mts +0 -4
  41. package/lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/type-utils/get.d.mts +0 -4
  42. package/lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/type-utils/join.d.mts +0 -4
  43. package/lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/type-utils/not.d.mts +0 -4
  44. package/lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/type-utils/pop.d.mts +0 -4
  45. package/lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/type-utils/split.d.mts +0 -7
  46. package/lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/type-utils/tail.d.mts +0 -4
  47. package/lib/node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/type-utils/writable.d.mts +0 -4
  48. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/meta-types/any.d.mts +0 -20
  49. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/meta-types/array.d.mts +0 -28
  50. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/meta-types/const.d.mts +0 -25
  51. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/meta-types/enum.d.mts +0 -25
  52. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/meta-types/exclusion/any.d.mts +0 -16
  53. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/meta-types/exclusion/array.d.mts +0 -20
  54. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/meta-types/exclusion/const.d.mts +0 -28
  55. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/meta-types/exclusion/enum.d.mts +0 -23
  56. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/meta-types/exclusion/index.d.mts +0 -24
  57. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/meta-types/exclusion/object.d.mts +0 -44
  58. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/meta-types/exclusion/primitive.d.mts +0 -16
  59. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/meta-types/exclusion/tuple.d.mts +0 -43
  60. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/meta-types/exclusion/union.d.mts +0 -13
  61. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/meta-types/exclusion/utils.d.mts +0 -35
  62. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/meta-types/intersection/any.d.mts +0 -23
  63. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/meta-types/intersection/array.d.mts +0 -24
  64. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/meta-types/intersection/const.d.mts +0 -34
  65. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/meta-types/intersection/enum.d.mts +0 -26
  66. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/meta-types/intersection/index.d.mts +0 -24
  67. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/meta-types/intersection/object.d.mts +0 -25
  68. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/meta-types/intersection/primitive.d.mts +0 -23
  69. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/meta-types/intersection/tuple.d.mts +0 -26
  70. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/meta-types/intersection/union.d.mts +0 -17
  71. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/meta-types/intersection/utils.d.mts +0 -10
  72. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/meta-types/never.d.mts +0 -9
  73. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/meta-types/object.d.mts +0 -49
  74. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/meta-types/primitive.d.mts +0 -26
  75. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/meta-types/resolve.d.mts +0 -22
  76. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/meta-types/tuple.d.mts +0 -36
  77. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/meta-types/type.d.mts +0 -18
  78. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/meta-types/union.d.mts +0 -23
  79. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/meta-types/utils.d.mts +0 -7
  80. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/utils/and.d.mts +0 -4
  81. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/utils/extends.d.mts +0 -7
  82. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/utils/if.d.mts +0 -4
  83. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/utils/intersectUnion.d.mts +0 -4
  84. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/utils/isNever.d.mts +0 -4
  85. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/utils/merge.d.mts +0 -6
  86. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/utils/not.d.mts +0 -4
  87. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/utils/or.d.mts +0 -4
  88. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/utils/prettify.d.mts +0 -6
  89. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/utils/tail.d.mts +0 -4
  90. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/utils/unionLast.d.mts +0 -6
  91. package/lib/node_modules/.pnpm/ts-algebra@2.0.0/node_modules/ts-algebra/lib/utils/unionPop.d.mts +0 -6
  92. package/lib/plugin.d.mts +0 -56
  93. package/lib/plugin.mjs +0 -93
  94. package/lib/rules/bin-name-casing.d.mts +0 -6
  95. package/lib/rules/bin-name-casing.mjs +0 -41
  96. package/lib/rules/exports-subpaths-style.d.mts +0 -18
  97. package/lib/rules/exports-subpaths-style.mjs +0 -71
  98. package/lib/rules/no-empty-fields.d.mts +0 -20
  99. package/lib/rules/no-empty-fields.mjs +0 -90
  100. package/lib/rules/no-local-dependencies.d.mts +0 -17
  101. package/lib/rules/no-local-dependencies.mjs +0 -47
  102. package/lib/rules/no-redundant-files.d.mts +0 -6
  103. package/lib/rules/no-redundant-files.mjs +0 -100
  104. package/lib/rules/no-redundant-publishConfig.d.mts +0 -6
  105. package/lib/rules/no-redundant-publishConfig.mjs +0 -50
  106. package/lib/rules/order-properties.d.mts +0 -25
  107. package/lib/rules/order-properties.mjs +0 -64
  108. package/lib/rules/repository-shorthand.d.mts +0 -18
  109. package/lib/rules/repository-shorthand.mjs +0 -111
  110. package/lib/rules/require-attribution.d.mts +0 -22
  111. package/lib/rules/require-attribution.mjs +0 -91
  112. package/lib/rules/require-properties.d.mts +0 -22
  113. package/lib/rules/require-properties.mjs +0 -74
  114. package/lib/rules/restrict-dependency-ranges.d.mts +0 -92
  115. package/lib/rules/restrict-dependency-ranges.mjs +0 -147
  116. package/lib/rules/restrict-private-properties.d.mts +0 -20
  117. package/lib/rules/restrict-private-properties.mjs +0 -53
  118. package/lib/rules/restrict-top-level-properties.d.mts +0 -39
  119. package/lib/rules/restrict-top-level-properties.mjs +0 -73
  120. package/lib/rules/scripts-name-casing.d.mts +0 -6
  121. package/lib/rules/scripts-name-casing.mjs +0 -44
  122. package/lib/rules/sort-collections.d.mts +0 -35
  123. package/lib/rules/sort-collections.mjs +0 -111
  124. package/lib/rules/specify-peers-locally.d.mts +0 -6
  125. package/lib/rules/specify-peers-locally.mjs +0 -63
  126. package/lib/rules/unique-dependencies.d.mts +0 -6
  127. package/lib/rules/unique-dependencies.mjs +0 -87
  128. package/lib/rules/valid-author.d.mts +0 -6
  129. package/lib/rules/valid-author.mjs +0 -59
  130. package/lib/rules/valid-peerDependenciesMeta-relationship.d.mts +0 -6
  131. package/lib/rules/valid-peerDependenciesMeta-relationship.mjs +0 -50
  132. package/lib/rules/valid-properties.d.mts +0 -9
  133. package/lib/rules/valid-properties.mjs +0 -57
  134. package/lib/rules/valid-repository-directory.d.mts +0 -6
  135. package/lib/rules/valid-repository-directory.mjs +0 -73
  136. package/lib/types/estree.d.mts +0 -8
  137. package/lib/utils/createSimpleRequirePropertyRule.d.mts +0 -48
  138. package/lib/utils/createSimpleRequirePropertyRule.mjs +0 -62
  139. package/lib/utils/createSimpleValidPropertyRule.d.mts +0 -17
  140. package/lib/utils/createSimpleValidPropertyRule.mjs +0 -57
  141. package/lib/utils/findPropertyWithKeyValue.d.mts +0 -10
  142. package/lib/utils/findPropertyWithKeyValue.mjs +0 -6
  143. package/lib/utils/formatErrors.d.mts +0 -9
  144. package/lib/utils/formatErrors.mjs +0 -12
  145. package/lib/utils/git/getGitAuthor.d.mts +0 -8
  146. package/lib/utils/git/getGitAuthor.mjs +0 -29
  147. package/lib/utils/git/index.d.mts +0 -2
  148. package/lib/utils/git/index.mjs +0 -2
  149. package/lib/utils/predicates/index.d.mts +0 -4
  150. package/lib/utils/predicates/index.mjs +0 -4
  151. package/lib/utils/predicates/isJSONStringLiteral.d.mts +0 -6
  152. package/lib/utils/predicates/isJSONStringLiteral.mjs +0 -6
  153. package/lib/utils/predicates/isNotNullish.d.mts +0 -4
  154. package/lib/utils/predicates/isNotNullish.mjs +0 -6
  155. package/lib/utils/predicates/isPackageJson.d.mts +0 -4
  156. package/lib/utils/predicates/isPackageJson.mjs +0 -4
@@ -0,0 +1,1737 @@
1
+ import { createRequire } from "node:module";
2
+ import * as parserJsonc from "jsonc-eslint-parser";
3
+ import { kebabCase } from "change-case";
4
+ import { fixRemoveArrayElement, fixRemoveObjectProperty } from "eslint-fix-utils";
5
+ import detectIndent from "detect-indent";
6
+ import { detectNewlineGraceful } from "detect-newline";
7
+ import sortObjectKeys from "sort-object-keys";
8
+ import sortPackageJson, { sortOrder } from "sort-package-json";
9
+ import { execFileSync } from "node:child_process";
10
+ import semver from "semver";
11
+ import { validateAuthor, validateBin, validateBrowser, validateBugs, validateBundleDependencies, validateConfig, validateContributors, validateCpu, validateDependencies, validateDescription, validateDevEngines, validateDirectories, validateEngines, validateExports, validateFiles, validateFunding, validateGypfile, validateHomepage, validateKeywords, validateLibc, validateLicense, validateMain, validateMan, validateName, validateOs, validatePackageManager, validatePeerDependenciesMeta, validatePrivate, validatePublishConfig, validateRepository, validateScripts, validateSideEffects, validateType, validateVersion, validateWorkspaces } from "package-json-validator";
12
+ import path from "node:path";
13
+ import { sep } from "node:path/posix";
14
+ import { findRootSync } from "@altano/repository-tools";
15
+ //#region src/utils/predicates/isJSONStringLiteral.ts
16
+ function isJSONStringLiteral(node) {
17
+ return node.type === "JSONLiteral" && typeof node.value === "string";
18
+ }
19
+ //#endregion
20
+ //#region src/utils/predicates/isNotNullish.ts
21
+ function isNotNullish(value) {
22
+ return value !== null && value !== void 0;
23
+ }
24
+ //#endregion
25
+ //#region src/utils/predicates/isPackageJson.ts
26
+ const isPackageJson = (filePath) => /(?:^|[/\\])package.json$/.test(filePath);
27
+ //#endregion
28
+ //#region src/createRule.ts
29
+ /**
30
+ * Rule options type is inferred from the JSON schema by [json-schema-to-ts](https://www.npmjs.com/package/json-schema-to-ts).
31
+ * If you're not satisfied with the inferred type, you may specify it manually in the first type parameter.
32
+ */
33
+ function createRule(rule) {
34
+ const ruleGroup = rule.meta.docs?.ruleGroup;
35
+ return {
36
+ create(context) {
37
+ if (!isPackageJson(context.filename)) return {};
38
+ return rule.create(context);
39
+ },
40
+ meta: {
41
+ ...rule.meta,
42
+ docs: {
43
+ ...rule.meta.docs,
44
+ url: `https://eslint-plugin-package-json.dev/rules/${ruleGroup ? `${ruleGroup}/` : ""}${rule.name}`
45
+ }
46
+ }
47
+ };
48
+ }
49
+ //#endregion
50
+ //#region src/rules/bin-name-casing.ts
51
+ const rule$18 = createRule({
52
+ create(context) {
53
+ return { "Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.value=bin]"(node) {
54
+ if (node.value.type === "JSONObjectExpression") for (const property of node.value.properties) {
55
+ const key = property.key;
56
+ const kebabCaseKey = kebabCase(key.value);
57
+ if (kebabCaseKey !== key.value) context.report({
58
+ data: { property: key.value },
59
+ messageId: "invalidCase",
60
+ node: key,
61
+ suggest: [{
62
+ data: { property: key.value },
63
+ fix: (fixer) => {
64
+ return fixer.replaceText(key, JSON.stringify(kebabCaseKey));
65
+ },
66
+ messageId: "convertToKebabCase"
67
+ }]
68
+ });
69
+ }
70
+ } };
71
+ },
72
+ meta: {
73
+ docs: {
74
+ category: "Stylistic",
75
+ description: "Enforce that names for bin properties are in kebab case."
76
+ },
77
+ hasSuggestions: true,
78
+ messages: {
79
+ convertToKebabCase: "Convert {{ property }} to kebab case.",
80
+ invalidCase: "Command name {{ property }} should be in kebab case."
81
+ },
82
+ schema: [],
83
+ type: "suggestion"
84
+ },
85
+ name: "bin-name-casing"
86
+ });
87
+ //#endregion
88
+ //#region src/rules/exports-subpaths-style.ts
89
+ function isImplicitFormat(node) {
90
+ if (node.type === "JSONLiteral") return true;
91
+ return node.properties.every((property) => !isJSONStringLiteral(property.key) || !property.key.value.startsWith("."));
92
+ }
93
+ const rule$17 = createRule({
94
+ create(context) {
95
+ const [{ prefer = "explicit" } = {}] = context.options;
96
+ function validateForExplicit(node) {
97
+ const { value } = node;
98
+ if (value.type !== "JSONLiteral" && value.type !== "JSONObjectExpression" || !isImplicitFormat(value)) return;
99
+ context.report({
100
+ fix(fixer) {
101
+ const valueText = context.sourceCode.getText(value);
102
+ const fixedValue = JSON.stringify({ ".": JSON.parse(valueText) }, null, 2);
103
+ return fixer.replaceText(value, fixedValue);
104
+ },
105
+ messageId: "preferExplicit",
106
+ node: value
107
+ });
108
+ }
109
+ function validateForImplicit(node) {
110
+ const { value } = node;
111
+ if (value.type !== "JSONObjectExpression") return;
112
+ if (value.properties.length !== 1 || !isJSONStringLiteral(value.properties[0].key) || value.properties[0].key.value !== ".") return;
113
+ const dotProperty = value.properties[0];
114
+ context.report({
115
+ fix(fixer) {
116
+ const valueText = context.sourceCode.getText(dotProperty.value);
117
+ const fixedValue = JSON.stringify(JSON.parse(valueText), null, 2);
118
+ return fixer.replaceText(value, fixedValue);
119
+ },
120
+ messageId: "preferImplicit",
121
+ node: value
122
+ });
123
+ }
124
+ return { JSONProperty(node) {
125
+ if (node.key.type !== "JSONLiteral" || node.key.value !== "exports" || node.parent.parent.parent.type !== "Program") return;
126
+ if (prefer === "explicit") validateForExplicit(node);
127
+ else validateForImplicit(node);
128
+ } };
129
+ },
130
+ meta: {
131
+ defaultOptions: [{ prefer: "explicit" }],
132
+ docs: {
133
+ category: "Stylistic",
134
+ description: "Enforce consistent format for the exports field (implicit or explicit subpaths).",
135
+ recommended: false
136
+ },
137
+ fixable: "code",
138
+ messages: {
139
+ preferExplicit: "Prefer explicit subpaths format with \".\" key for single root export.",
140
+ preferImplicit: "Prefer implicit format without \".\" key for single root export."
141
+ },
142
+ schema: [{
143
+ additionalProperties: false,
144
+ properties: { prefer: {
145
+ description: "Specifies which exports format to enforce.",
146
+ enum: ["implicit", "explicit"],
147
+ type: "string"
148
+ } },
149
+ type: "object"
150
+ }],
151
+ type: "suggestion"
152
+ },
153
+ name: "exports-subpaths-style"
154
+ });
155
+ //#endregion
156
+ //#region src/rules/no-empty-fields.ts
157
+ const getDataAndMessageId = (node) => {
158
+ switch (node.type) {
159
+ case "JSONArrayExpression": return {
160
+ data: { expressionType: "array" },
161
+ messageId: "emptyExpression"
162
+ };
163
+ case "JSONObjectExpression": return {
164
+ data: { expressionType: "object" },
165
+ messageId: "emptyExpression"
166
+ };
167
+ case "JSONProperty": return {
168
+ data: { field: node.key.value },
169
+ messageId: "emptyFields"
170
+ };
171
+ }
172
+ };
173
+ const report = (context, node) => {
174
+ const { data, messageId } = getDataAndMessageId(node);
175
+ context.report({
176
+ data,
177
+ messageId,
178
+ node,
179
+ suggest: [{
180
+ fix: node.type === "JSONProperty" ? fixRemoveObjectProperty(context, node) : fixRemoveArrayElement(context, node, node.parent),
181
+ messageId: "remove"
182
+ }]
183
+ });
184
+ };
185
+ const getNode = (node) => {
186
+ return node.parent.type === "JSONProperty" ? node.parent : node;
187
+ };
188
+ const getTopLevelProperty = (node) => {
189
+ let n = node;
190
+ while (n.parent.parent?.parent?.type !== void 0 && n.parent.parent.parent.type !== "Program") n = n.parent;
191
+ return n.type === "JSONProperty" ? n.key : void 0;
192
+ };
193
+ const defaultIgnoreProperties = ["files"];
194
+ const rule$16 = createRule({
195
+ create(context) {
196
+ const ignoreProperties = new Set(context.options[0]?.ignoreProperties ?? defaultIgnoreProperties);
197
+ return {
198
+ JSONArrayExpression(node) {
199
+ const topLevelProperty = getTopLevelProperty(node);
200
+ if (!topLevelProperty) return;
201
+ if (!node.elements.length) {
202
+ const topLevelPropertyName = topLevelProperty.value;
203
+ if (!ignoreProperties.has(topLevelPropertyName)) report(context, getNode(node));
204
+ }
205
+ },
206
+ JSONObjectExpression(node) {
207
+ const topLevelProperty = getTopLevelProperty(node);
208
+ if (!topLevelProperty) return;
209
+ if (!node.properties.length) {
210
+ const topLevelPropertyName = topLevelProperty.value;
211
+ if (!ignoreProperties.has(topLevelPropertyName)) report(context, getNode(node));
212
+ }
213
+ }
214
+ };
215
+ },
216
+ meta: {
217
+ defaultOptions: [{ ignoreProperties: defaultIgnoreProperties }],
218
+ docs: {
219
+ category: "Best Practices",
220
+ description: "Reports on unnecessary empty arrays and objects.",
221
+ recommended: true
222
+ },
223
+ hasSuggestions: true,
224
+ messages: {
225
+ emptyExpression: "This {{ expressionType }} does nothing and can be removed.",
226
+ emptyFields: "The field '{{ field }}' does nothing and can be removed.",
227
+ remove: "Remove this empty field."
228
+ },
229
+ schema: [{
230
+ additionalProperties: false,
231
+ properties: { ignoreProperties: {
232
+ description: "Array of top-level properties to ignore.",
233
+ items: { type: "string" },
234
+ type: "array"
235
+ } },
236
+ type: "object"
237
+ }],
238
+ type: "suggestion"
239
+ },
240
+ name: "no-empty-fields"
241
+ });
242
+ //#endregion
243
+ //#region src/rules/no-local-dependencies.ts
244
+ const isLocalDependency = (value) => value.startsWith("file:") || value.startsWith("link:") || value.startsWith("./") || value.startsWith("../") || value.startsWith(".\\") || value.startsWith("..\\");
245
+ const rule$15 = createRule({
246
+ create(context) {
247
+ const ignorePrivate = context.options[0]?.ignorePrivate ?? true;
248
+ let isPrivate = false;
249
+ let dependencyNodes = [];
250
+ return {
251
+ "Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.type=JSONLiteral][value.type=JSONLiteral][key.value=private]"(node) {
252
+ if (node.value.value === true) isPrivate = true;
253
+ },
254
+ "Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.type=JSONLiteral][value.type=JSONObjectExpression][key.value=dependencies]"(node) {
255
+ dependencyNodes = node.value.properties;
256
+ },
257
+ "Program:exit"() {
258
+ if (ignorePrivate && isPrivate) return;
259
+ for (const dependencyPropertyNode of dependencyNodes) {
260
+ const dependencyValue = dependencyPropertyNode.value;
261
+ if (isJSONStringLiteral(dependencyValue) && isLocalDependency(dependencyValue.value)) context.report({
262
+ data: { name: dependencyValue.value },
263
+ messageId: "localDependencyFound",
264
+ node: dependencyValue
265
+ });
266
+ }
267
+ }
268
+ };
269
+ },
270
+ meta: {
271
+ defaultOptions: [{ ignorePrivate: true }],
272
+ docs: { description: "Requires that dependencies do not use local file paths, which will likely result in errors when installing from a registry." },
273
+ messages: { localDependencyFound: "Local dependency \"{{ name }}\" is not allowed." },
274
+ schema: [{
275
+ additionalProperties: false,
276
+ properties: { ignorePrivate: {
277
+ description: "Determines if this rule should be enforced when the package's `private` property is `true`.",
278
+ type: "boolean"
279
+ } },
280
+ type: "object"
281
+ }],
282
+ type: "problem"
283
+ },
284
+ name: "no-local-dependencies"
285
+ });
286
+ //#endregion
287
+ //#region src/rules/no-redundant-files.ts
288
+ const defaultFiles = [
289
+ /^(\.\/)?LICEN(C|S)E(\.|$)/i,
290
+ /^(\.\/)?README(\.|$)/i,
291
+ /^(\.\/)?package\.json$/i
292
+ ];
293
+ const wildcardsRegex = /[*?[\]{}]/;
294
+ const cachedRegex = /* @__PURE__ */ new Map();
295
+ const getCachedLocalFileRegex = (filename) => {
296
+ if (wildcardsRegex.test(filename)) return null;
297
+ const baseFilename = filename.replace("./", "");
298
+ let regex = cachedRegex.get(baseFilename);
299
+ if (regex) return regex;
300
+ else {
301
+ regex = new RegExp(`^(./)?${baseFilename}$`, "i");
302
+ cachedRegex.set(baseFilename, regex);
303
+ return regex;
304
+ }
305
+ };
306
+ const rule$14 = createRule({
307
+ create(context) {
308
+ const entryCache = {
309
+ bin: [],
310
+ files: []
311
+ };
312
+ const report = (elements, index, messageId) => {
313
+ const element = elements[index];
314
+ if (isNotNullish(element) && isJSONStringLiteral(element)) context.report({
315
+ data: { file: element.value },
316
+ messageId,
317
+ node: element,
318
+ suggest: [{
319
+ fix: fixRemoveArrayElement(context, index, elements),
320
+ messageId: "remove"
321
+ }]
322
+ });
323
+ };
324
+ return {
325
+ "Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.value=bin]"(node) {
326
+ const binValue = node.value;
327
+ if (isJSONStringLiteral(binValue)) entryCache.bin.push(binValue.value);
328
+ else if (binValue.type === "JSONObjectExpression") {
329
+ for (const prop of binValue.properties) if (isJSONStringLiteral(prop.value)) entryCache.bin.push(prop.value.value);
330
+ }
331
+ },
332
+ "Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.value=files]"(node) {
333
+ if (node.value.type !== "JSONArrayExpression") return;
334
+ const seen = /* @__PURE__ */ new Set();
335
+ const elements = node.value.elements;
336
+ entryCache.files = elements;
337
+ for (const [index, element] of elements.entries()) if (isNotNullish(element) && isJSONStringLiteral(element)) {
338
+ if (seen.has(element.value)) report(elements, index, "duplicate");
339
+ else seen.add(element.value);
340
+ for (const defaultFile of defaultFiles) if (defaultFile.test(element.value)) report(elements, index, "unnecessaryDefault");
341
+ }
342
+ },
343
+ "Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.value=main]"(node) {
344
+ if (isJSONStringLiteral(node.value)) entryCache.main = node.value.value;
345
+ },
346
+ "Program:exit"() {
347
+ const files = entryCache.files;
348
+ if (files.length === 0) return;
349
+ const validations = [{
350
+ files: entryCache.main ? [entryCache.main] : [],
351
+ messageId: "unnecessaryMain"
352
+ }, {
353
+ files: entryCache.bin,
354
+ messageId: "unnecessaryBin"
355
+ }];
356
+ for (const validation of validations) for (const fileToCheck of validation.files) for (const [index, fileEntry] of files.entries()) if (isNotNullish(fileEntry) && isJSONStringLiteral(fileEntry)) {
357
+ if (getCachedLocalFileRegex(fileEntry.value)?.test(fileToCheck)) report(files, index, validation.messageId);
358
+ }
359
+ }
360
+ };
361
+ },
362
+ meta: {
363
+ docs: {
364
+ category: "Best Practices",
365
+ description: "Prevents adding unnecessary / redundant files.",
366
+ recommended: true
367
+ },
368
+ hasSuggestions: true,
369
+ messages: {
370
+ duplicate: "Files has more than one entry for \"{{file}}\".",
371
+ remove: "Remove this redundant entry.",
372
+ unnecessaryBin: `Explicitly declaring "{{file}}" in "files" is unnecessary; it's included in "bin".`,
373
+ unnecessaryDefault: `Explicitly declaring "{{file}}" in "files" is unnecessary; it's included by default.`,
374
+ unnecessaryMain: `Explicitly declaring "{{file}}" in "files" is unnecessary; it's the "main" entry.`
375
+ },
376
+ schema: [],
377
+ type: "suggestion"
378
+ },
379
+ name: "no-redundant-files"
380
+ });
381
+ //#endregion
382
+ //#region src/rules/no-redundant-publishConfig.ts
383
+ const rule$13 = createRule({
384
+ create(context) {
385
+ let packageName;
386
+ let publishConfigAccessProperty;
387
+ return {
388
+ "Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.value=name]"(node) {
389
+ if (isJSONStringLiteral(node.value)) packageName = node.value.value;
390
+ },
391
+ "Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.value=publishConfig]"(node) {
392
+ if (node.value.type !== "JSONObjectExpression") return;
393
+ for (const property of node.value.properties) if (isJSONStringLiteral(property.key) && property.key.value === "access") {
394
+ publishConfigAccessProperty = property;
395
+ break;
396
+ }
397
+ },
398
+ "Program:exit"() {
399
+ if (!packageName || !publishConfigAccessProperty) return;
400
+ if (!packageName.startsWith("@")) context.report({
401
+ messageId: "redundantAccess",
402
+ node: publishConfigAccessProperty,
403
+ suggest: [{
404
+ fix: fixRemoveObjectProperty(context, publishConfigAccessProperty),
405
+ messageId: "removeAccess"
406
+ }]
407
+ });
408
+ }
409
+ };
410
+ },
411
+ meta: {
412
+ docs: {
413
+ category: "Best Practices",
414
+ description: "Warns when publishConfig.access is used in unscoped packages.",
415
+ recommended: true
416
+ },
417
+ hasSuggestions: true,
418
+ messages: {
419
+ redundantAccess: "'publishConfig.access' is redundant for unscoped packages - they are always public.",
420
+ removeAccess: "Remove the redundant 'access' field."
421
+ },
422
+ schema: [],
423
+ type: "suggestion"
424
+ },
425
+ name: "no-redundant-publishConfig"
426
+ });
427
+ //#endregion
428
+ //#region src/rules/order-properties.ts
429
+ const rule$12 = createRule({
430
+ create(context) {
431
+ return { "Program:exit"() {
432
+ const { ast, text } = context.sourceCode;
433
+ const { order = "sort-package-json" } = context.options[0] ?? {};
434
+ const requiredOrder = order === "sort-package-json" ? sortOrder : order;
435
+ const json = JSON.parse(text);
436
+ const orderedNonStandardKeys = Object.keys(json).filter((key) => !requiredOrder.includes(key)).sort();
437
+ const orderedSource = sortObjectKeys(json, [...requiredOrder, ...orderedNonStandardKeys]);
438
+ const orderedKeys = Object.keys(orderedSource);
439
+ const { properties } = ast.body[0].expression;
440
+ for (let i = 0; i < properties.length; i += 1) {
441
+ const { value } = properties[i].key;
442
+ if (value === orderedKeys[i]) continue;
443
+ context.report({
444
+ data: { property: value },
445
+ fix(fixer) {
446
+ const { indent, type } = detectIndent(text);
447
+ const endCharacters = text.endsWith("\n") ? "\n" : "";
448
+ const newline = detectNewlineGraceful(text);
449
+ let result = JSON.stringify(orderedSource, null, type === "tab" ? " " : indent) + endCharacters;
450
+ if (newline === "\r\n") result = result.replace(/\n/g, newline);
451
+ return fixer.replaceText(context.sourceCode.ast, result);
452
+ },
453
+ loc: properties[i].loc,
454
+ messageId: "incorrectOrder"
455
+ });
456
+ }
457
+ } };
458
+ },
459
+ meta: {
460
+ defaultOptions: [{ order: "sort-package-json" }],
461
+ docs: {
462
+ category: "Stylistic",
463
+ description: "Enforces that package properties are declared in a consistent order."
464
+ },
465
+ fixable: "code",
466
+ messages: { incorrectOrder: "Top-level property `{{property}}` is not ordered in the standard way." },
467
+ schema: [{
468
+ additionalProperties: false,
469
+ properties: { order: {
470
+ anyOf: [{
471
+ enum: ["sort-package-json"],
472
+ type: ["string"]
473
+ }, {
474
+ items: { type: ["string"] },
475
+ type: ["array"]
476
+ }],
477
+ description: "Specifies the sorting order of top-level properties."
478
+ } },
479
+ type: "object"
480
+ }],
481
+ type: "layout"
482
+ },
483
+ name: "order-properties"
484
+ });
485
+ //#endregion
486
+ //#region src/utils/findPropertyWithKeyValue.ts
487
+ function findPropertyWithKeyValue(properties, value) {
488
+ return properties.find((property) => property.key.type === "JSONLiteral" && property.key.value === value);
489
+ }
490
+ //#endregion
491
+ //#region src/rules/repository-shorthand.ts
492
+ const providerRegexes = {
493
+ bitbucket: /^(?:git\+)?(?:ssh:\/\/git@|http?s:\/\/)?(?:www\.)?bitbucket\.org\//,
494
+ gist: /^(?:git\+)?(?:ssh:\/\/git@|http?s:\/\/)?(?:www\.)?gist\.github\.com\//,
495
+ github: /^(?:git\+)?(?:ssh:\/\/git@|http?s:\/\/)?(?:www\.)?github\.com\//,
496
+ gitlab: /^(?:git\+)?(?:ssh:\/\/git@|http?s:\/\/)?(?:www\.)?gitlab\.com\//
497
+ };
498
+ const providerUrls = {
499
+ bitbucket: "https://bitbucket.org/",
500
+ gist: "https://gist.github.com/",
501
+ github: "https://github.com/",
502
+ gitlab: "https://gitlab.com/"
503
+ };
504
+ const providers = Object.keys(providerRegexes);
505
+ const isProvider = (value) => value in providerRegexes;
506
+ const cleanUrl = (url, provider) => url.replace(providerRegexes[provider], "").replace(/\.git$/, "");
507
+ const getProviderFromUrl = (url) => {
508
+ return providers.find((provider) => providerRegexes[provider].test(url));
509
+ };
510
+ const createShorthand = (url, provider) => {
511
+ return `${provider}:${cleanUrl(url, provider)}`;
512
+ };
513
+ const createUrl = (shorthand) => {
514
+ if (shorthand.includes(":")) {
515
+ const [provider, repo] = shorthand.split(":", 2);
516
+ if (isProvider(provider)) return `${providerUrls[provider]}${repo}`;
517
+ }
518
+ return `${providerUrls.github}${shorthand}`;
519
+ };
520
+ const rule$11 = createRule({
521
+ create(context) {
522
+ const [{ form = "object" } = {}] = context.options;
523
+ function validateRepositoryForObject(node) {
524
+ if (isJSONStringLiteral(node.value)) context.report({
525
+ fix(fixer) {
526
+ if (!isJSONStringLiteral(node.value) || node.value.value.split("/").filter(Boolean).length !== 2) return null;
527
+ return fixer.replaceText(node.value, JSON.stringify({
528
+ type: "git",
529
+ url: createUrl(node.value.value)
530
+ }, null, 2));
531
+ },
532
+ messageId: "preferObject",
533
+ node: node.value
534
+ });
535
+ }
536
+ function validateRepositoryForShorthand(node) {
537
+ if (isJSONStringLiteral(node.value)) {
538
+ const { value } = node.value;
539
+ const provider = getProviderFromUrl(value);
540
+ if (provider) context.report({
541
+ fix(fixer) {
542
+ return fixer.replaceText(node.value, JSON.stringify(createShorthand(value, provider)));
543
+ },
544
+ messageId: "preferShorthand",
545
+ node: node.value
546
+ });
547
+ return;
548
+ }
549
+ if (node.value.type !== "JSONObjectExpression") return;
550
+ const { properties } = node.value;
551
+ if (findPropertyWithKeyValue(properties, "directory")) return;
552
+ const typeProperty = findPropertyWithKeyValue(properties, "type");
553
+ if (typeProperty?.value.type !== "JSONLiteral" || typeProperty.value.value !== "git") return;
554
+ const urlProperty = findPropertyWithKeyValue(properties, "url");
555
+ if (urlProperty?.value.type !== "JSONLiteral" || typeof urlProperty.value.value !== "string") return;
556
+ const url = urlProperty.value.value;
557
+ const provider = getProviderFromUrl(url);
558
+ if (provider) context.report({
559
+ fix(fixer) {
560
+ return fixer.replaceText(node.value, JSON.stringify(createShorthand(url, provider)));
561
+ },
562
+ messageId: "preferShorthand",
563
+ node: node.value
564
+ });
565
+ }
566
+ return { JSONProperty(node) {
567
+ if (node.key.type !== "JSONLiteral" || node.key.value !== "repository" || node.parent.parent.parent.type !== "Program") return;
568
+ if (form === "shorthand") validateRepositoryForShorthand(node);
569
+ else validateRepositoryForObject(node);
570
+ } };
571
+ },
572
+ meta: {
573
+ defaultOptions: [{ form: "object" }],
574
+ docs: {
575
+ category: "Best Practices",
576
+ description: "Enforce either object or shorthand declaration for repository.",
577
+ recommended: true
578
+ },
579
+ fixable: "code",
580
+ messages: {
581
+ preferObject: "Prefer an object locator for a repository.",
582
+ preferShorthand: "Prefer a shorthand locator for a supported repository provider."
583
+ },
584
+ schema: [{
585
+ additionalProperties: false,
586
+ properties: { form: {
587
+ description: "Specifies which repository form to enforce.",
588
+ enum: ["object", "shorthand"],
589
+ type: ["string"]
590
+ } },
591
+ type: "object"
592
+ }],
593
+ type: "suggestion"
594
+ },
595
+ name: "repository-shorthand"
596
+ });
597
+ //#endregion
598
+ //#region src/rules/require-attribution.ts
599
+ const rule$10 = createRule({
600
+ create(context) {
601
+ const preferContributorsOnly = context.options[0]?.preferContributorsOnly ?? false;
602
+ const ignorePrivate = context.options[0]?.ignorePrivate ?? true;
603
+ let authorPropertyNode;
604
+ let contributorsPropertyNode;
605
+ let isPrivatePackage = false;
606
+ return {
607
+ "Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.value=author]"(node) {
608
+ authorPropertyNode = node;
609
+ },
610
+ "Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.value=contributors]"(node) {
611
+ contributorsPropertyNode = node;
612
+ const contributorsValue = node.value;
613
+ if (contributorsValue.type !== "JSONArrayExpression" || contributorsValue.elements.every((element) => !element)) context.report({
614
+ messageId: "noContributors",
615
+ node
616
+ });
617
+ },
618
+ "Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.value=private]"(node) {
619
+ if (node.value.type === "JSONLiteral" && node.value.value === true) isPrivatePackage = true;
620
+ },
621
+ "Program:exit"() {
622
+ if (ignorePrivate && isPrivatePackage) return;
623
+ if (preferContributorsOnly) {
624
+ if (authorPropertyNode) context.report({
625
+ messageId: "contributorsOnly",
626
+ node: authorPropertyNode,
627
+ suggest: [{
628
+ fix: fixRemoveObjectProperty(context, authorPropertyNode),
629
+ messageId: "removeAuthor"
630
+ }]
631
+ });
632
+ if (!contributorsPropertyNode) context.report({
633
+ loc: {
634
+ column: 0,
635
+ line: 1
636
+ },
637
+ messageId: "missingContributor"
638
+ });
639
+ } else if (!authorPropertyNode && !contributorsPropertyNode) context.report({
640
+ loc: {
641
+ column: 0,
642
+ line: 1
643
+ },
644
+ messageId: "missing"
645
+ });
646
+ }
647
+ };
648
+ },
649
+ meta: {
650
+ defaultOptions: [{
651
+ ignorePrivate: true,
652
+ preferContributorsOnly: false
653
+ }],
654
+ docs: {
655
+ category: "Best Practices",
656
+ description: "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.",
657
+ recommended: true
658
+ },
659
+ hasSuggestions: true,
660
+ messages: {
661
+ contributorsOnly: "Only `contributors` should be defined for attribution.",
662
+ missing: "Property attribution is required. Either `author` or `contributors` should be defined.",
663
+ missingContributor: "Property attribution is required. `contributors` should be defined.",
664
+ noContributors: "At least one contributor should be defined.",
665
+ removeAuthor: "Remove `author`."
666
+ },
667
+ schema: [{
668
+ additionalProperties: false,
669
+ properties: {
670
+ ignorePrivate: {
671
+ description: "Skip attribution requirements for packages with `\"private\": true`.",
672
+ type: "boolean"
673
+ },
674
+ preferContributorsOnly: {
675
+ description: "Require that only `contributors` is present, and `author` is not defined.",
676
+ type: "boolean"
677
+ }
678
+ },
679
+ type: "object"
680
+ }],
681
+ type: "suggestion"
682
+ },
683
+ name: "require-attribution"
684
+ });
685
+ //#endregion
686
+ //#region src/utils/createSimpleRequirePropertyRule.ts
687
+ /**
688
+ * Given a top-level property name, create a rule that requires that property to be present.
689
+ * Optionally, include it in the recommended config.
690
+ * Note: this will only create a basic require rule, with no options. If you need
691
+ * to create a more complex rule, create it in its own file.
692
+ */
693
+ const createSimpleRequirePropertyRule = (propertyName, { category, fixValue, ignorePrivateDefault = false, isRecommended } = {}) => {
694
+ const ruleName = `require-${propertyName}`;
695
+ return {
696
+ rule: createRule({
697
+ create(context) {
698
+ const enforceForPrivate = context.settings.packageJson?.enforceForPrivate;
699
+ const ignorePrivate = context.options[0]?.ignorePrivate ?? (typeof enforceForPrivate === "boolean" ? !enforceForPrivate : ignorePrivateDefault);
700
+ return { "Program > JSONExpressionStatement > JSONObjectExpression"(node) {
701
+ if (ignorePrivate && node.properties.some((property) => isJSONStringLiteral(property.key) && property.key.value === "private" && property.value.type === "JSONLiteral" && property.value.value === true)) return;
702
+ if (node.properties.every((property) => !(isJSONStringLiteral(property.key) && property.key.value === propertyName))) {
703
+ const resolvedValue = typeof fixValue === "function" ? fixValue() : fixValue;
704
+ context.report({
705
+ data: { property: propertyName },
706
+ fix: resolvedValue === void 0 ? void 0 : function* (fixer) {
707
+ yield fixer.insertTextAfterRange([0, 1], `\n "${propertyName}": ${JSON.stringify(resolvedValue, null, 2).split("\n").join("\n ")}`);
708
+ yield node.properties.length > 0 ? fixer.insertTextAfterRange([0, 1], ",") : fixer.insertTextAfterRange([0, 1], "\n");
709
+ },
710
+ loc: {
711
+ column: 0,
712
+ line: 1
713
+ },
714
+ messageId: "missing"
715
+ });
716
+ }
717
+ } };
718
+ },
719
+ meta: {
720
+ docs: {
721
+ category,
722
+ description: `Requires the \`${propertyName}\` property to be present.`,
723
+ recommended: isRecommended,
724
+ ruleGroup: "require-properties"
725
+ },
726
+ fixable: fixValue === void 0 ? void 0 : "code",
727
+ messages: { missing: "Property '{{property}}' is required." },
728
+ schema: propertyName === "private" ? [] : [{
729
+ additionalProperties: false,
730
+ properties: { ignorePrivate: {
731
+ default: ignorePrivateDefault,
732
+ description: "Determines if this rule should be enforced when the package's `private` property is `true`.",
733
+ type: "boolean"
734
+ } },
735
+ type: "object"
736
+ }],
737
+ type: "suggestion"
738
+ },
739
+ name: ruleName
740
+ }),
741
+ ruleName
742
+ };
743
+ };
744
+ //#endregion
745
+ //#region src/utils/git/getGitAuthor.ts
746
+ let cachedAuthor;
747
+ const getGitAuthor = () => {
748
+ if (cachedAuthor === null) return;
749
+ else if (cachedAuthor !== void 0) return cachedAuthor;
750
+ try {
751
+ const name = execFileSync("git", [
752
+ "config",
753
+ "--get",
754
+ "user.name"
755
+ ], { encoding: "utf8" }).trim();
756
+ const email = execFileSync("git", [
757
+ "config",
758
+ "--get",
759
+ "user.email"
760
+ ], { encoding: "utf8" }).trim();
761
+ cachedAuthor = {
762
+ name,
763
+ ...email && { email }
764
+ };
765
+ } catch {
766
+ cachedAuthor = null;
767
+ return;
768
+ }
769
+ return cachedAuthor;
770
+ };
771
+ const rules$2 = Object.fromEntries([
772
+ ["author", { fixValue: getGitAuthor }],
773
+ ["bin"],
774
+ ["browser"],
775
+ ["bugs", { ignorePrivateDefault: true }],
776
+ ["bundleDependencies"],
777
+ ["config"],
778
+ ["contributors"],
779
+ ["cpu"],
780
+ ["dependencies"],
781
+ ["description", { isRecommended: true }],
782
+ ["devDependencies"],
783
+ ["devEngines"],
784
+ ["directories"],
785
+ ["engines"],
786
+ ["exports", {
787
+ ignorePrivateDefault: true,
788
+ isRecommended: true
789
+ }],
790
+ ["files", {
791
+ ignorePrivateDefault: true,
792
+ isRecommended: true
793
+ }],
794
+ ["funding"],
795
+ ["gypfile"],
796
+ ["homepage", { ignorePrivateDefault: true }],
797
+ ["keywords", { ignorePrivateDefault: true }],
798
+ ["libc"],
799
+ ["license", {
800
+ ignorePrivateDefault: true,
801
+ isRecommended: true
802
+ }],
803
+ ["main"],
804
+ ["man"],
805
+ ["module"],
806
+ ["name", {
807
+ ignorePrivateDefault: true,
808
+ isRecommended: true
809
+ }],
810
+ ["optionalDependencies"],
811
+ ["os"],
812
+ ["packageManager"],
813
+ ["peerDependencies"],
814
+ ["peerDependenciesMeta"],
815
+ ["private", { fixValue: false }],
816
+ ["publishConfig", { ignorePrivateDefault: true }],
817
+ ["repository", {
818
+ ignorePrivateDefault: true,
819
+ isRecommended: true
820
+ }],
821
+ ["scripts"],
822
+ ["sideEffects", {
823
+ ignorePrivateDefault: true,
824
+ isRecommended: true
825
+ }],
826
+ ["type", {
827
+ fixValue: "commonjs",
828
+ isRecommended: true
829
+ }],
830
+ ["types"],
831
+ ["version", {
832
+ ignorePrivateDefault: true,
833
+ isRecommended: true
834
+ }]
835
+ ].map(([propertyName, options]) => {
836
+ const { rule, ruleName } = createSimpleRequirePropertyRule(propertyName, options);
837
+ return [ruleName, rule];
838
+ }));
839
+ //#endregion
840
+ //#region src/rules/restrict-dependency-ranges.ts
841
+ const DEPENDENCY_TYPES = [
842
+ "dependencies",
843
+ "devDependencies",
844
+ "optionalDependencies",
845
+ "peerDependencies"
846
+ ];
847
+ const RANGE_TYPES = [
848
+ "caret",
849
+ "pin",
850
+ "tilde"
851
+ ];
852
+ const schemaOptions = {
853
+ additionalProperties: false,
854
+ properties: {
855
+ forDependencyTypes: {
856
+ description: "Apply a range type restriction for an entire group of dependencies by which type of dependencies they belong to.",
857
+ items: { enum: DEPENDENCY_TYPES },
858
+ type: "array"
859
+ },
860
+ forPackages: {
861
+ description: "The exact name of a package, or a regex pattern used to match a group of packages by name.",
862
+ items: { type: "string" },
863
+ type: "array"
864
+ },
865
+ forVersions: {
866
+ description: "Apply a restriction to a specific semver range.",
867
+ type: "string"
868
+ },
869
+ rangeType: {
870
+ description: "Identifies which range type or types you want to apply to packages that match any of the other match options (or all dependencies if no other options are provided).",
871
+ oneOf: [{ enum: RANGE_TYPES }, {
872
+ items: { enum: RANGE_TYPES },
873
+ type: "array"
874
+ }]
875
+ }
876
+ },
877
+ required: ["rangeType"],
878
+ type: "object"
879
+ };
880
+ const SYMBOLS = {
881
+ caret: "^",
882
+ pin: "",
883
+ tilde: "~"
884
+ };
885
+ /**
886
+ * Given the original version, update it to use the correct range type.
887
+ */
888
+ const changeVersionRange = (version, rangeType) => {
889
+ if (/^workspace:[~^*]$/.test(version)) switch (rangeType) {
890
+ case "caret": return "workspace:^";
891
+ case "pin": return "workspace:*";
892
+ default: return "workspace:~";
893
+ }
894
+ return version.replace(/^(workspace:)?(\^|~|<=?|>=?)?/, `$1${SYMBOLS[rangeType]}`);
895
+ };
896
+ /**
897
+ * Check if the version is in a form that this rule supports.
898
+ */
899
+ const isVersionSupported = (version) => {
900
+ if (/^workspace:[*^~]$/.test(version)) return true;
901
+ const rawVersion = version.replace(/^workspace:/, "");
902
+ return !!semver.validRange(rawVersion);
903
+ };
904
+ const capitalize = (str) => {
905
+ return str.charAt(0).toUpperCase() + str.slice(1);
906
+ };
907
+ const rule$9 = createRule({
908
+ create(context) {
909
+ if (!context.options[0]) return {};
910
+ const optionsArray = (Array.isArray(context.options[0]) ? context.options[0].toReversed() : [context.options[0]]).map((option) => ({
911
+ ...option,
912
+ forPackages: option.forPackages?.map((pattern) => new RegExp(pattern)),
913
+ rangeTypes: Array.isArray(option.rangeType) ? option.rangeType : [option.rangeType]
914
+ }));
915
+ return { "Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.type=JSONLiteral][value.type=JSONObjectExpression]"(node) {
916
+ const dependencyType = node.key.value;
917
+ if (!DEPENDENCY_TYPES.includes(dependencyType)) return;
918
+ for (const property of node.value.properties) {
919
+ if (!isJSONStringLiteral(property.key) || !isJSONStringLiteral(property.value)) continue;
920
+ const name = property.key.value;
921
+ const version = property.value.value;
922
+ if (!isVersionSupported(version)) continue;
923
+ const isPinned = !!semver.parse(version) || version === "workspace:*";
924
+ const isTildeRange = !!semver.validRange(version) && version.startsWith("~") || version.startsWith("workspace:~");
925
+ const isCaretRange = !!semver.validRange(version) && version.startsWith("^") || version.startsWith("workspace:^");
926
+ for (const options of optionsArray) {
927
+ if (options.forDependencyTypes && !options.forDependencyTypes.includes(dependencyType)) continue;
928
+ if (options.forPackages) {
929
+ if (!options.forPackages.some((packageNameRegex) => packageNameRegex.test(name))) continue;
930
+ }
931
+ if (options.forVersions && (/^workspace:[^~*]?$/.test(version) || version !== "*" && !semver.satisfies(version.replace(/(?:workspace:)?[^~]?/, ""), options.forVersions))) continue;
932
+ const rangeTypes = options.rangeTypes;
933
+ if (version === "*") {
934
+ context.report({
935
+ data: { rangeTypes: rangeTypes.join(", ") },
936
+ messageId: "wrongRangeType",
937
+ node: property.value
938
+ });
939
+ break;
940
+ }
941
+ if (!rangeTypes.some((rangeType) => {
942
+ switch (rangeType) {
943
+ case "caret": return isCaretRange;
944
+ case "pin": return isPinned;
945
+ case "tilde": return isTildeRange;
946
+ }
947
+ })) context.report({
948
+ data: { rangeTypes: rangeTypes.join(", ") },
949
+ messageId: "wrongRangeType",
950
+ node: property.value,
951
+ suggest: rangeTypes.map((rangeType) => ({
952
+ fix(fixer) {
953
+ return fixer.replaceText(property.value, `"${changeVersionRange(version, rangeType)}"`);
954
+ },
955
+ messageId: `changeTo${capitalize(rangeType)}`
956
+ }))
957
+ });
958
+ break;
959
+ }
960
+ }
961
+ } };
962
+ },
963
+ meta: {
964
+ defaultOptions: [[]],
965
+ docs: { description: "Restricts the range of dependencies to allow or disallow specific types of ranges." },
966
+ hasSuggestions: true,
967
+ messages: {
968
+ changeToCaret: "Change to use a caret range.",
969
+ changeToPin: "Pin the version.",
970
+ changeToTilde: "Change to use a tilde range.",
971
+ wrongRangeType: "This dependency is using the wrong range type. Acceptable range type(s): {{rangeTypes}}"
972
+ },
973
+ schema: [{ oneOf: [schemaOptions, {
974
+ description: "Array of configuration options, specifying range requirements.",
975
+ items: schemaOptions,
976
+ type: "array"
977
+ }] }],
978
+ type: "suggestion"
979
+ },
980
+ name: "restrict-dependency-ranges"
981
+ });
982
+ //#endregion
983
+ //#region src/rules/restrict-private-properties.ts
984
+ const defaultBlockedProperties = ["files", "publishConfig"];
985
+ const rule$8 = createRule({
986
+ create(context) {
987
+ const blockedProperties = context.options[0]?.blockedProperties ?? defaultBlockedProperties;
988
+ return { "Program > JSONExpressionStatement > JSONObjectExpression"(node) {
989
+ if (node.properties.every((property) => !(isJSONStringLiteral(property.key) && property.key.value === "private" && property.value.type === "JSONLiteral" && property.value.value === true))) return;
990
+ for (const property of node.properties) if (isJSONStringLiteral(property.key) && blockedProperties.includes(property.key.value)) {
991
+ const isEmpty = property.value.type === "JSONArrayExpression" && property.value.elements.length === 0 || property.value.type === "JSONObjectExpression" && property.value.properties.length === 0;
992
+ context.report({
993
+ data: { property: property.key.value },
994
+ messageId: "unnecessaryProperty",
995
+ node: property,
996
+ ...isEmpty ? { fix: fixRemoveObjectProperty(context, property) } : { suggest: [{
997
+ data: { property: property.key.value },
998
+ fix: fixRemoveObjectProperty(context, property),
999
+ messageId: "removePropertySuggestion"
1000
+ }] }
1001
+ });
1002
+ }
1003
+ } };
1004
+ },
1005
+ meta: {
1006
+ defaultOptions: [{ blockedProperties: defaultBlockedProperties }],
1007
+ docs: {
1008
+ category: "Best Practices",
1009
+ description: "Disallows unnecessary properties in private packages.",
1010
+ recommended: false
1011
+ },
1012
+ fixable: "code",
1013
+ hasSuggestions: true,
1014
+ messages: {
1015
+ removePropertySuggestion: "Remove the '{{ property }}' field.",
1016
+ unnecessaryProperty: "The '{{ property }}' field is unnecessary in private packages and can be removed."
1017
+ },
1018
+ schema: [{
1019
+ additionalProperties: false,
1020
+ properties: { blockedProperties: {
1021
+ description: "Array of property names to disallow in private packages.",
1022
+ items: { type: "string" },
1023
+ type: "array"
1024
+ } },
1025
+ type: "object"
1026
+ }],
1027
+ type: "suggestion"
1028
+ },
1029
+ name: "restrict-private-properties"
1030
+ });
1031
+ //#endregion
1032
+ //#region src/rules/restrict-top-level-properties.ts
1033
+ const rule$7 = createRule({
1034
+ create(context) {
1035
+ const banList = Object.fromEntries(
1036
+ /* v8 ignore next - not possible to cover due to presence of `defaultOptions` */
1037
+ (context.options[0]?.ban ?? []).map((entry) => typeof entry === "string" ? [entry, ""] : [entry.property, entry.message ?? ""])
1038
+ );
1039
+ return { "Program > JSONExpressionStatement > JSONObjectExpression"(node) {
1040
+ for (const property of node.properties) {
1041
+ if (!isJSONStringLiteral(property.key)) continue;
1042
+ const propertyName = property.key.value;
1043
+ if (Object.hasOwn(banList, propertyName)) {
1044
+ const customMessage = banList[propertyName];
1045
+ context.report({
1046
+ data: {
1047
+ customMessage: customMessage ? `: ${customMessage}` : "",
1048
+ property: propertyName
1049
+ },
1050
+ messageId: "bannedProperty",
1051
+ node: property.key,
1052
+ suggest: [{
1053
+ fix: fixRemoveObjectProperty(context, property),
1054
+ messageId: "removePropertySuggestion"
1055
+ }]
1056
+ });
1057
+ }
1058
+ }
1059
+ } };
1060
+ },
1061
+ meta: {
1062
+ defaultOptions: [{ ban: [] }],
1063
+ docs: {
1064
+ category: "Best Practices",
1065
+ description: "Disallows specified top-level properties in package.json.",
1066
+ recommended: false
1067
+ },
1068
+ hasSuggestions: true,
1069
+ messages: {
1070
+ bannedProperty: "The `{{ property }}` property is not allowed{{ customMessage }}",
1071
+ removePropertySuggestion: "Remove the property."
1072
+ },
1073
+ schema: [{
1074
+ additionalProperties: false,
1075
+ properties: { ban: {
1076
+ description: "List of top-level properties to ban. Each entry can be a property name string or an object with a property name and an optional custom message.",
1077
+ items: { oneOf: [{ type: "string" }, {
1078
+ additionalProperties: false,
1079
+ properties: {
1080
+ message: {
1081
+ description: "Custom message to append to the error report.",
1082
+ type: "string"
1083
+ },
1084
+ property: {
1085
+ description: "The top-level property name to ban.",
1086
+ type: "string"
1087
+ }
1088
+ },
1089
+ required: ["property"],
1090
+ type: "object"
1091
+ }] },
1092
+ type: "array"
1093
+ } },
1094
+ type: "object"
1095
+ }],
1096
+ type: "suggestion"
1097
+ },
1098
+ name: "restrict-top-level-properties"
1099
+ });
1100
+ //#endregion
1101
+ //#region src/rules/scripts-name-casing.ts
1102
+ const BUILT_IN_SCRIPTS_IN_CAMEL_CASE = /* @__PURE__ */ new Set(["prepublishOnly"]);
1103
+ const rule$6 = createRule({
1104
+ create(context) {
1105
+ return { "Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.value=scripts]"(node) {
1106
+ if (node.value.type === "JSONObjectExpression") for (const property of node.value.properties) {
1107
+ const keyNode = property.key;
1108
+ const key = keyNode.value;
1109
+ if (BUILT_IN_SCRIPTS_IN_CAMEL_CASE.has(key)) continue;
1110
+ const kebabCaseKey = key.split(":").map((segment) => kebabCase(segment)).join(":");
1111
+ if (kebabCaseKey !== key) context.report({
1112
+ data: { property: key },
1113
+ messageId: "invalidCase",
1114
+ node: keyNode,
1115
+ suggest: [{
1116
+ data: { property: key },
1117
+ fix: (fixer) => {
1118
+ return fixer.replaceText(keyNode, JSON.stringify(kebabCaseKey));
1119
+ },
1120
+ messageId: "convertToKebabCase"
1121
+ }]
1122
+ });
1123
+ }
1124
+ } };
1125
+ },
1126
+ meta: {
1127
+ docs: {
1128
+ category: "Stylistic",
1129
+ description: "Enforce that names for `scripts` are in kebab case (optionally separated by colons)."
1130
+ },
1131
+ hasSuggestions: true,
1132
+ messages: {
1133
+ convertToKebabCase: "Convert {{ property }} to kebab case.",
1134
+ invalidCase: "Script name {{ property }} should be in kebab case."
1135
+ },
1136
+ schema: [],
1137
+ type: "suggestion"
1138
+ },
1139
+ name: "scripts-name-casing"
1140
+ });
1141
+ //#endregion
1142
+ //#region src/rules/sort-collections.ts
1143
+ const defaultCollections = /* @__PURE__ */ new Set([
1144
+ "config",
1145
+ "dependencies",
1146
+ "devDependencies",
1147
+ "exports",
1148
+ "optionalDependencies",
1149
+ "overrides",
1150
+ "peerDependencies",
1151
+ "peerDependenciesMeta",
1152
+ "scripts"
1153
+ ]);
1154
+ const rule$5 = createRule({
1155
+ create(context) {
1156
+ const toSort = new Map((context.options[0] ?? Array.from(defaultCollections)).map((entry) => typeof entry === "string" ? [entry, null] : [entry.key, entry.order]));
1157
+ return { "JSONProperty:exit"(node) {
1158
+ const { key: nodeKey, value: collection } = node;
1159
+ if (nodeKey.type !== "JSONLiteral" || collection.type !== "JSONObjectExpression") return;
1160
+ const keyPartsReversed = [nodeKey.value];
1161
+ for (let currNode = node.parent; currNode; currNode = currNode.parent) if (currNode.type === "JSONProperty" && currNode.key.type === "JSONLiteral") keyPartsReversed.push(currNode.key.value);
1162
+ else if (currNode.type === "JSONArrayExpression") return;
1163
+ const key = keyPartsReversed.reverse().join(".");
1164
+ const customOrder = toSort.get(key);
1165
+ if (customOrder === void 0) return;
1166
+ const currentOrder = collection.properties;
1167
+ const isScripts = keyPartsReversed.at(-1) === "scripts";
1168
+ let naturalCompare;
1169
+ if (isScripts) {
1170
+ const scriptsSource = context.sourceCode.getText(node);
1171
+ const { scripts: sortedScripts } = sortPackageJson(JSON.parse(`{${scriptsSource}}`));
1172
+ const lifecycleIndex = new Map(Object.keys(sortedScripts).map((k, i) => [k, i]));
1173
+ naturalCompare = (a, b) => (lifecycleIndex.get(a) ?? 0) - (lifecycleIndex.get(b) ?? 0);
1174
+ } else naturalCompare = (a, b) => a > b ? 1 : -1;
1175
+ const orderIndex = new Map((customOrder ?? []).map((k, i) => [k, i]));
1176
+ const rank = (k) => orderIndex.get(k) ?? orderIndex.size;
1177
+ const desiredOrder = currentOrder.toSorted((a, b) => {
1178
+ const aKey = a.key.value;
1179
+ const bKey = b.key.value;
1180
+ const ai = rank(aKey);
1181
+ const bi = rank(bKey);
1182
+ if (ai !== bi) return ai - bi;
1183
+ return naturalCompare(aKey, bKey);
1184
+ });
1185
+ if (currentOrder.some((property, i) => desiredOrder[i] !== property)) context.report({
1186
+ data: { key },
1187
+ fix(fixer) {
1188
+ const { text } = context.sourceCode;
1189
+ const { indent, type } = detectIndent(text);
1190
+ const newline = detectNewlineGraceful(text);
1191
+ const indentUnit = type === "tab" ? " " : indent || " ";
1192
+ const jsonLines = JSON.stringify(desiredOrder.reduce((out, property) => {
1193
+ out[property.key.value] = JSON.parse(context.sourceCode.getText(property.value));
1194
+ return out;
1195
+ }, {}), null, indentUnit).split("\n");
1196
+ const collectionStartLine = collection.loc.start.line;
1197
+ const lineText = context.sourceCode.lines[collectionStartLine - 1];
1198
+ const leadingWhitespaceMatch = /^\s*/.exec(lineText);
1199
+ const leadingWhitespace = leadingWhitespaceMatch ? leadingWhitespaceMatch[0] : "";
1200
+ const result = jsonLines.map((l, i) => i === 0 ? l : leadingWhitespace + l).join(newline);
1201
+ return fixer.replaceText(collection, result);
1202
+ },
1203
+ loc: collection.loc,
1204
+ messageId: customOrder ? "unsortedOrder" : isScripts ? "unsortedScripts" : "unsortedKeys",
1205
+ node
1206
+ });
1207
+ } };
1208
+ },
1209
+ meta: {
1210
+ defaultOptions: [Array.from(defaultCollections)],
1211
+ docs: {
1212
+ category: "Best Practices",
1213
+ description: "Selected collections must be in a consistent order (lexicographical for most; lifecycle-aware for scripts).",
1214
+ recommended: true
1215
+ },
1216
+ fixable: "code",
1217
+ messages: {
1218
+ unsortedKeys: "Entries in '{{ key }}' are not in lexicographical order",
1219
+ unsortedOrder: "Entries in '{{ key }}' are not in the specified order",
1220
+ unsortedScripts: "Entries in 'scripts' are not in lexicographical order and grouped by lifecycles"
1221
+ },
1222
+ schema: [{
1223
+ description: "Array of package properties to require sorting. Provide a string to sort that collection lexicographically (lifecycle-aware for `scripts`), or an object to sort it by a specified order.",
1224
+ items: { anyOf: [{ type: "string" }, {
1225
+ additionalProperties: false,
1226
+ properties: {
1227
+ key: {
1228
+ description: "The collection property to sort.",
1229
+ type: "string"
1230
+ },
1231
+ order: {
1232
+ description: "The order to sort the collection by. Keys not listed are appended in lexicographical order.",
1233
+ items: { type: "string" },
1234
+ type: "array",
1235
+ uniqueItems: true
1236
+ }
1237
+ },
1238
+ required: ["key", "order"],
1239
+ type: "object"
1240
+ }] },
1241
+ type: "array"
1242
+ }],
1243
+ type: "layout"
1244
+ },
1245
+ name: "sort-collections"
1246
+ });
1247
+ //#endregion
1248
+ //#region src/rules/specify-peers-locally.ts
1249
+ const rule$4 = createRule({
1250
+ create(context) {
1251
+ const devDependencyNames = /* @__PURE__ */ new Set();
1252
+ let devDependenciesObjectNode;
1253
+ const peerDependencyMap = {};
1254
+ return {
1255
+ "Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.type=JSONLiteral][value.type=JSONObjectExpression][key.value=devDependencies]"(node) {
1256
+ devDependenciesObjectNode = node.value;
1257
+ for (const devDependencyPropertyNode of node.value.properties) {
1258
+ const dependencyKey = devDependencyPropertyNode.key;
1259
+ if (isJSONStringLiteral(dependencyKey)) devDependencyNames.add(dependencyKey.value);
1260
+ }
1261
+ },
1262
+ "Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.type=JSONLiteral][value.type=JSONObjectExpression][key.value=peerDependencies]"(node) {
1263
+ for (const peerDependencyPropertyNode of node.value.properties) {
1264
+ const dependencyKey = peerDependencyPropertyNode.key;
1265
+ if (isJSONStringLiteral(dependencyKey)) peerDependencyMap[dependencyKey.value] = peerDependencyPropertyNode;
1266
+ }
1267
+ },
1268
+ "Program:exit"() {
1269
+ for (const [peerDependencyName, peerDependencyNode] of Object.entries(peerDependencyMap)) if (!devDependencyNames.has(peerDependencyName)) {
1270
+ const peerDependencyValue = peerDependencyNode.value;
1271
+ context.report({
1272
+ data: { name: peerDependencyName },
1273
+ messageId: "devDependencyNotDefined",
1274
+ node: peerDependencyNode,
1275
+ suggest: devDependenciesObjectNode && isJSONStringLiteral(peerDependencyValue) ? [{
1276
+ data: { name: peerDependencyName },
1277
+ fix: (fixer) => {
1278
+ const updatedDevDependencies = {
1279
+ ...JSON.parse(context.sourceCode.getText(devDependenciesObjectNode)),
1280
+ [peerDependencyName]: peerDependencyValue.value
1281
+ };
1282
+ const sortedDevDependencies = Object.fromEntries(Object.entries(updatedDevDependencies).sort((a, b) => a[0] > b[0] ? 1 : -1));
1283
+ return fixer.replaceText(devDependenciesObjectNode, JSON.stringify(sortedDevDependencies, null, 2).split("\n").join("\n "));
1284
+ },
1285
+ messageId: "addToDevDependencies"
1286
+ }] : []
1287
+ });
1288
+ }
1289
+ }
1290
+ };
1291
+ },
1292
+ meta: {
1293
+ docs: {
1294
+ description: "Requires that all peer dependencies are also declared as dev dependencies",
1295
+ recommended: true
1296
+ },
1297
+ hasSuggestions: true,
1298
+ messages: {
1299
+ addToDevDependencies: "Add \"{{ name }}\" to `devDependencies`",
1300
+ devDependencyNotDefined: "Peer dependency \"{{ name }}\" is not also declared in `devDependencies`."
1301
+ },
1302
+ schema: [],
1303
+ type: "problem"
1304
+ },
1305
+ name: "specify-peers-locally"
1306
+ });
1307
+ //#endregion
1308
+ //#region src/rules/unique-dependencies.ts
1309
+ const dependencyPropertyNames = /* @__PURE__ */ new Set([
1310
+ "bundledDependencies",
1311
+ "bundleDependencies",
1312
+ "dependencies",
1313
+ "devDependencies",
1314
+ "optionalDependencies",
1315
+ "overrides",
1316
+ "peerDependencies"
1317
+ ]);
1318
+ const rule$3 = createRule({
1319
+ create(context) {
1320
+ const dependenciesCache = {
1321
+ dependencies: [],
1322
+ devDependencies: [],
1323
+ peerDependencies: []
1324
+ };
1325
+ const trackForCrossGroupUniqueness = Object.keys(dependenciesCache);
1326
+ function check(elements, getNodeToRemove) {
1327
+ const seen = /* @__PURE__ */ new Set();
1328
+ for (const element of elements.filter(isNotNullish).filter(isJSONStringLiteral).reverse()) if (seen.has(element.value)) report(element);
1329
+ else seen.add(element.value);
1330
+ function report(node) {
1331
+ const removal = getNodeToRemove(node);
1332
+ context.report({
1333
+ messageId: "overridden",
1334
+ node,
1335
+ suggest: [{
1336
+ fix: removal.type === "JSONProperty" ? fixRemoveObjectProperty(context, removal) : fixRemoveArrayElement(context, removal, elements),
1337
+ messageId: "remove"
1338
+ }]
1339
+ });
1340
+ }
1341
+ }
1342
+ return {
1343
+ "Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.type=JSONLiteral]"(node) {
1344
+ if (!dependencyPropertyNames.has(node.key.value)) return;
1345
+ const nodeValueType = node.value.type;
1346
+ if (nodeValueType === "JSONArrayExpression") check(node.value.elements, (element) => element);
1347
+ if (nodeValueType === "JSONObjectExpression") {
1348
+ check(node.value.properties.map((property) => property.key), (property) => property.parent);
1349
+ if (trackForCrossGroupUniqueness.includes(node.key.value)) dependenciesCache[node.key.value] = node.value.properties;
1350
+ }
1351
+ },
1352
+ "Program:exit"() {
1353
+ const dependencyNames = new Set(dependenciesCache.dependencies.map((node) => node.key).filter(isJSONStringLiteral).map((dependencyNameNode) => dependencyNameNode.value));
1354
+ if (!dependencyNames.size) return;
1355
+ for (const dependencyType of ["devDependencies", "peerDependencies"]) {
1356
+ const otherDependencies = dependenciesCache[dependencyType];
1357
+ for (const otherDependencyNode of otherDependencies) {
1358
+ const otherDependencyKey = otherDependencyNode.key;
1359
+ if (isJSONStringLiteral(otherDependencyKey) && dependencyNames.has(otherDependencyKey.value)) context.report({
1360
+ messageId: "crossGroupDuplicate",
1361
+ node: otherDependencyNode,
1362
+ suggest: [{
1363
+ fix: fixRemoveObjectProperty(context, otherDependencyNode),
1364
+ messageId: "remove"
1365
+ }]
1366
+ });
1367
+ }
1368
+ }
1369
+ }
1370
+ };
1371
+ },
1372
+ meta: {
1373
+ docs: {
1374
+ category: "Best Practices",
1375
+ description: "Checks a dependency isn't specified more than once (i.e. in `dependencies` and `devDependencies`)",
1376
+ recommended: true
1377
+ },
1378
+ hasSuggestions: true,
1379
+ messages: {
1380
+ crossGroupDuplicate: "Dependency is also declared in \"dependencies\" and is redundant",
1381
+ overridden: "Dependency is overridden by a duplicate entry later on",
1382
+ remove: "Remove this redundant dependency"
1383
+ },
1384
+ schema: [],
1385
+ type: "suggestion"
1386
+ },
1387
+ name: "unique-dependencies"
1388
+ });
1389
+ //#endregion
1390
+ //#region src/rules/valid-author.ts
1391
+ const rule$2 = createRule({
1392
+ create(context) {
1393
+ const createFixer = (propertyName, node) => {
1394
+ const author = getGitAuthor();
1395
+ if (!author) return;
1396
+ let mergedAuthor = author;
1397
+ const originalValue = JSON.parse(context.sourceCode.getText(node));
1398
+ if (originalValue && typeof originalValue === "object" && !Array.isArray(originalValue)) mergedAuthor = {
1399
+ name: author.name,
1400
+ ...originalValue
1401
+ };
1402
+ switch (propertyName) {
1403
+ case "author": return (fixer) => fixer.replaceText(node, JSON.stringify(mergedAuthor, null, 2).split("\n").join("\n "));
1404
+ case "email": return (fixer) => fixer.replaceText(node, JSON.stringify(author.email));
1405
+ case "name": return (fixer) => fixer.replaceText(node, JSON.stringify(author.name));
1406
+ default: return;
1407
+ }
1408
+ };
1409
+ const reportIssues = (result, node, propertyName) => {
1410
+ if (result.errorMessages.length === 0) return;
1411
+ if (result.issues.length) for (const issue of result.issues) context.report({
1412
+ data: { error: issue.message },
1413
+ fix: propertyName === void 0 ? void 0 : createFixer(propertyName, node),
1414
+ messageId: "validationError",
1415
+ node
1416
+ });
1417
+ const childrenWithIssues = result.childResults.filter((childResult) => childResult.errorMessages.length);
1418
+ if (node.type === "JSONObjectExpression" && childrenWithIssues.length) for (const childResult of childrenWithIssues) {
1419
+ const childNode = node.properties[childResult.index];
1420
+ const childPropertyName = childNode.key;
1421
+ reportIssues(childResult, childNode.value, isJSONStringLiteral(childPropertyName) ? childPropertyName.value : void 0);
1422
+ }
1423
+ };
1424
+ return { "Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.value=author]"(node) {
1425
+ const valueNode = node.value;
1426
+ const result = validateAuthor(JSON.parse(context.sourceCode.getText(valueNode)));
1427
+ reportIssues(result, valueNode, "author");
1428
+ } };
1429
+ },
1430
+ meta: {
1431
+ docs: {
1432
+ category: "Best Practices",
1433
+ description: "Enforce that the `author` property is valid.",
1434
+ recommended: true,
1435
+ ruleGroup: "valid-properties"
1436
+ },
1437
+ fixable: "code",
1438
+ messages: { validationError: `Invalid author: {{ error }}` },
1439
+ schema: [],
1440
+ type: "problem"
1441
+ },
1442
+ name: "valid-author"
1443
+ });
1444
+ //#endregion
1445
+ //#region src/rules/valid-peerDependenciesMeta-relationship.ts
1446
+ const rule$1 = createRule({
1447
+ create(context) {
1448
+ const peerDependencies = /* @__PURE__ */ new Set();
1449
+ const peerDependenciesMeta = /* @__PURE__ */ new Map();
1450
+ return {
1451
+ "Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.value=peerDependencies]"(node) {
1452
+ if (node.value.type === "JSONObjectExpression") {
1453
+ for (const property of node.value.properties) if (isJSONStringLiteral(property.key)) peerDependencies.add(property.key.value);
1454
+ }
1455
+ },
1456
+ "Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.value=peerDependenciesMeta]"(node) {
1457
+ if (node.value.type === "JSONObjectExpression") {
1458
+ for (const property of node.value.properties) if (isJSONStringLiteral(property.key)) peerDependenciesMeta.set(property.key.value, property);
1459
+ }
1460
+ },
1461
+ "Program:exit"() {
1462
+ for (const [dependencyName, propertyNode] of peerDependenciesMeta) if (!peerDependencies.has(dependencyName)) context.report({
1463
+ data: { dependencyName },
1464
+ messageId: "unnecessaryPeerDependency",
1465
+ node: propertyNode,
1466
+ suggest: [{
1467
+ fix: fixRemoveObjectProperty(context, propertyNode),
1468
+ messageId: "removePeerDependencyMeta"
1469
+ }]
1470
+ });
1471
+ }
1472
+ };
1473
+ },
1474
+ meta: {
1475
+ docs: {
1476
+ category: "Best Practices",
1477
+ description: "Enforces that any dependencies declared in `peerDependenciesMeta` are also defined in the package's `peerDependencies`.",
1478
+ recommended: true
1479
+ },
1480
+ hasSuggestions: true,
1481
+ messages: {
1482
+ removePeerDependencyMeta: "Remove from `peerDependenciesMeta`.",
1483
+ unnecessaryPeerDependency: "Dependency '{{ dependencyName }}' is declared in `peerDependenciesMeta` but not in `peerDependencies`."
1484
+ },
1485
+ schema: [],
1486
+ type: "problem"
1487
+ },
1488
+ name: "valid-peerDependenciesMeta-relationship"
1489
+ });
1490
+ //#endregion
1491
+ //#region src/utils/createSimpleValidPropertyRule.ts
1492
+ /**
1493
+ * Given a top-level property name, and a validation function, create a rule that validates the property using the validation function.
1494
+ * These rules will always be included in the recommended config.
1495
+ * Note: this will only create a basic validation rule, with no options. If you need
1496
+ * to create a more complex rule, create it in its own file.
1497
+ */
1498
+ const createSimpleValidPropertyRule = (propertyName, validationFunction, aliases = []) => {
1499
+ const ruleName = `valid-${propertyName}`;
1500
+ const propertyNames = [propertyName, ...aliases];
1501
+ return {
1502
+ rule: createRule({
1503
+ create(context) {
1504
+ const reportIssues = (result, node) => {
1505
+ if (result.errorMessages.length === 0) return;
1506
+ if (result.issues.length) for (const issue of result.issues) context.report({
1507
+ data: { error: issue.message },
1508
+ messageId: "validationError",
1509
+ node
1510
+ });
1511
+ const childrenWithIssues = result.childResults.filter((childResult) => childResult.errorMessages.length);
1512
+ if (node.type === "JSONObjectExpression" && childrenWithIssues.length) for (const childResult of childrenWithIssues) {
1513
+ const childNode = node.properties[childResult.index];
1514
+ reportIssues(childResult, childNode.value);
1515
+ }
1516
+ else if (node.type === "JSONArrayExpression" && childrenWithIssues.length) for (const childResult of childrenWithIssues) {
1517
+ const childNode = node.elements[childResult.index];
1518
+ if (childNode) reportIssues(childResult, childNode);
1519
+ }
1520
+ };
1521
+ return propertyNames.reduce((acc, name) => {
1522
+ acc[`Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.value=${name}]`] = (node) => {
1523
+ const valueNode = node.value;
1524
+ const result = validationFunction(JSON.parse(context.sourceCode.getText(valueNode)));
1525
+ reportIssues(result, valueNode);
1526
+ };
1527
+ return acc;
1528
+ }, {});
1529
+ },
1530
+ meta: {
1531
+ docs: {
1532
+ category: "Best Practices",
1533
+ description: `Enforce that the \`${propertyName}\`${aliases.length ? ` (also ${aliases.map((alias) => `\`${alias}\``).join(", ")})` : ""} property is valid.`,
1534
+ recommended: true,
1535
+ ruleGroup: "valid-properties"
1536
+ },
1537
+ messages: { validationError: `Invalid ${propertyName}: {{ error }}` },
1538
+ schema: [],
1539
+ type: "problem"
1540
+ },
1541
+ name: ruleName
1542
+ }),
1543
+ ruleName
1544
+ };
1545
+ };
1546
+ /** All basic valid- flavor rules */
1547
+ const rules$1 = Object.fromEntries([
1548
+ ["bin", validateBin],
1549
+ ["browser", validateBrowser],
1550
+ ["bugs", validateBugs],
1551
+ ["bundleDependencies", {
1552
+ aliases: ["bundledDependencies"],
1553
+ validator: validateBundleDependencies
1554
+ }],
1555
+ ["config", validateConfig],
1556
+ ["contributors", validateContributors],
1557
+ ["cpu", validateCpu],
1558
+ ["description", validateDescription],
1559
+ ["dependencies", validateDependencies],
1560
+ ["devDependencies", validateDependencies],
1561
+ ["devEngines", validateDevEngines],
1562
+ ["directories", validateDirectories],
1563
+ ["engines", validateEngines],
1564
+ ["exports", validateExports],
1565
+ ["files", validateFiles],
1566
+ ["funding", validateFunding],
1567
+ ["gypfile", validateGypfile],
1568
+ ["homepage", validateHomepage],
1569
+ ["keywords", validateKeywords],
1570
+ ["libc", validateLibc],
1571
+ ["license", validateLicense],
1572
+ ["main", validateMain],
1573
+ ["man", validateMan],
1574
+ ["module", validateMain],
1575
+ ["name", validateName],
1576
+ ["optionalDependencies", validateDependencies],
1577
+ ["os", validateOs],
1578
+ ["packageManager", validatePackageManager],
1579
+ ["peerDependencies", validateDependencies],
1580
+ ["peerDependenciesMeta", validatePeerDependenciesMeta],
1581
+ ["private", validatePrivate],
1582
+ ["publishConfig", validatePublishConfig],
1583
+ ["repository", validateRepository],
1584
+ ["scripts", validateScripts],
1585
+ ["sideEffects", validateSideEffects],
1586
+ ["type", validateType],
1587
+ ["version", validateVersion],
1588
+ ["workspaces", validateWorkspaces]
1589
+ ].map(([propertyName, validationFunctionOrOptions]) => {
1590
+ let validationFunction;
1591
+ let aliases = [];
1592
+ if (typeof validationFunctionOrOptions === "object") {
1593
+ validationFunction = validationFunctionOrOptions.validator;
1594
+ aliases = validationFunctionOrOptions.aliases;
1595
+ } else validationFunction = validationFunctionOrOptions;
1596
+ const { rule, ruleName } = createSimpleValidPropertyRule(propertyName, validationFunction, aliases);
1597
+ return [ruleName, rule];
1598
+ }));
1599
+ //#endregion
1600
+ //#region src/rules/valid-repository-directory.ts
1601
+ /**
1602
+ * Checks if the child path appears at the end of the parent path.
1603
+ * @example '/a/b/c', 'c' => true
1604
+ * @example '/a/b/c', 'b/c' => true
1605
+ * @example '/a/b/c', 'b' => false
1606
+ * @example '/a/b/c', 'd' => false
1607
+ */
1608
+ const pathEndsWith = (parent, child) => {
1609
+ if (parent === child) return true;
1610
+ const segments = parent.split(path.sep);
1611
+ let pathToCheck = "";
1612
+ return segments.reverse().some((segment) => {
1613
+ pathToCheck = path.join(segment, pathToCheck);
1614
+ if (pathToCheck === child) return true;
1615
+ });
1616
+ };
1617
+ let repoRootCache = null;
1618
+ const rule = createRule({
1619
+ create(context) {
1620
+ return { "Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.value=repository][value.type=JSONObjectExpression]"(node) {
1621
+ const directoryProperty = findPropertyWithKeyValue(node.value.properties, "directory");
1622
+ if (directoryProperty?.value.type !== "JSONLiteral" || typeof directoryProperty.value.value !== "string") return;
1623
+ const directoryValue = directoryProperty.value.value;
1624
+ const fileDirectory = path.normalize(path.dirname(context.filename));
1625
+ let repositoryRoot;
1626
+ if (repoRootCache && fileDirectory.startsWith(repoRootCache)) repositoryRoot = repoRootCache;
1627
+ else {
1628
+ repositoryRoot = findRootSync(fileDirectory);
1629
+ if (repositoryRoot) repoRootCache = path.normalize(repositoryRoot);
1630
+ }
1631
+ if (repositoryRoot) {
1632
+ const expected = path.relative(repositoryRoot, fileDirectory).replaceAll(path.sep, sep);
1633
+ if (expected !== directoryValue) context.report({
1634
+ messageId: "mismatched",
1635
+ node: directoryProperty.value,
1636
+ suggest: [{
1637
+ data: { expected },
1638
+ fix(fixer) {
1639
+ return fixer.replaceText(directoryProperty.value, `"${expected}"`);
1640
+ },
1641
+ messageId: "replace"
1642
+ }]
1643
+ });
1644
+ } else if (!pathEndsWith(fileDirectory, path.normalize(directoryValue))) context.report({
1645
+ messageId: "mismatched",
1646
+ node: directoryProperty.value
1647
+ });
1648
+ } };
1649
+ },
1650
+ meta: {
1651
+ docs: {
1652
+ category: "Best Practices",
1653
+ description: "Enforce that if repository directory is specified, it matches the path to the package.json file",
1654
+ recommended: true
1655
+ },
1656
+ hasSuggestions: true,
1657
+ messages: {
1658
+ mismatched: "Directory does not match package.json directory.",
1659
+ replace: "Replace with '{{ expected }}'."
1660
+ },
1661
+ schema: [],
1662
+ type: "suggestion"
1663
+ },
1664
+ name: "valid-repository-directory"
1665
+ });
1666
+ //#endregion
1667
+ //#region src/plugin.ts
1668
+ const require = createRequire(import.meta.url);
1669
+ const rules = {
1670
+ "bin-name-casing": rule$18,
1671
+ "exports-subpaths-style": rule$17,
1672
+ "no-empty-fields": rule$16,
1673
+ "no-local-dependencies": rule$15,
1674
+ "no-redundant-files": rule$14,
1675
+ "no-redundant-publishConfig": rule$13,
1676
+ "order-properties": rule$12,
1677
+ "require-attribution": rule$10,
1678
+ ...rules$2,
1679
+ "repository-shorthand": rule$11,
1680
+ "restrict-dependency-ranges": rule$9,
1681
+ "restrict-private-properties": rule$8,
1682
+ "restrict-top-level-properties": rule$7,
1683
+ "scripts-name-casing": rule$6,
1684
+ "sort-collections": rule$5,
1685
+ "specify-peers-locally": rule$4,
1686
+ "unique-dependencies": rule$3,
1687
+ "valid-author": rule$2,
1688
+ ...rules$1,
1689
+ "valid-peerDependenciesMeta-relationship": rule$1,
1690
+ "valid-repository-directory": rule
1691
+ };
1692
+ const recommendedRules = { ...Object.fromEntries(Object.entries(rules).filter(([, rule]) => rule.meta.docs?.recommended).map(([name]) => ["package-json/" + name, "error"])) };
1693
+ const recommendedPublishableRules = {
1694
+ ...recommendedRules,
1695
+ ...Object.fromEntries(Object.entries(rules).filter(([, rule]) => rule.meta.docs?.category === "Publishable").map(([name]) => ["package-json/" + name, "error"]))
1696
+ };
1697
+ const stylisticRules = { ...Object.fromEntries(Object.entries(rules).filter(([, rule]) => rule.meta.docs?.category === "Stylistic").map(([name]) => ["package-json/" + name, "error"])) };
1698
+ const { name, version } = require("../package.json");
1699
+ const plugin = {
1700
+ configs: {
1701
+ recommended: {
1702
+ files: ["**/package.json"],
1703
+ languageOptions: { parser: parserJsonc },
1704
+ name: "package-json/recommended",
1705
+ plugins: { get "package-json"() {
1706
+ return plugin;
1707
+ } },
1708
+ rules: recommendedRules
1709
+ },
1710
+ /** @deprecated use the recommended config instead. this config will be removed in a future major release */
1711
+ "recommended-publishable": {
1712
+ files: ["**/package.json"],
1713
+ languageOptions: { parser: parserJsonc },
1714
+ name: "package-json/recommended-publishable",
1715
+ plugins: { get "package-json"() {
1716
+ return plugin;
1717
+ } },
1718
+ rules: recommendedPublishableRules
1719
+ },
1720
+ stylistic: {
1721
+ files: ["**/package.json"],
1722
+ languageOptions: { parser: parserJsonc },
1723
+ name: "package-json/stylistic",
1724
+ plugins: { get "package-json"() {
1725
+ return plugin;
1726
+ } },
1727
+ rules: stylisticRules
1728
+ }
1729
+ },
1730
+ meta: {
1731
+ name,
1732
+ version
1733
+ },
1734
+ rules
1735
+ };
1736
+ //#endregion
1737
+ export { plugin as t };