eslint-plugin-package-json 1.6.2 → 1.7.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/dist/index.mjs
CHANGED
|
@@ -9,7 +9,9 @@ import sortPackageJson, { sortOrder } from "sort-package-json";
|
|
|
9
9
|
import { execFileSync } from "node:child_process";
|
|
10
10
|
import semver from "semver";
|
|
11
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 fs from "node:fs";
|
|
12
13
|
import path from "node:path";
|
|
14
|
+
import process from "node:process";
|
|
13
15
|
import { sep } from "node:path/posix";
|
|
14
16
|
import { findRootSync } from "@altano/repository-tools";
|
|
15
17
|
//#region src/utils/predicates/isJSONStringLiteral.ts
|
|
@@ -242,12 +244,21 @@ const rule$16 = createRule({
|
|
|
242
244
|
//#endregion
|
|
243
245
|
//#region src/rules/no-local-dependencies.ts
|
|
244
246
|
const isLocalDependency = (value) => value.startsWith("file:") || value.startsWith("link:") || value.startsWith("./") || value.startsWith("../") || value.startsWith(".\\") || value.startsWith("..\\");
|
|
247
|
+
const getBundledDependencyNames = (value) => {
|
|
248
|
+
const names = /* @__PURE__ */ new Set();
|
|
249
|
+
if (value?.type === "JSONArrayExpression") for (const element of value.elements.filter(isNotNullish).filter(isJSONStringLiteral)) names.add(element.value);
|
|
250
|
+
return names;
|
|
251
|
+
};
|
|
245
252
|
const rule$15 = createRule({
|
|
246
253
|
create(context) {
|
|
247
254
|
const ignorePrivate = context.options[0]?.ignorePrivate ?? true;
|
|
248
255
|
let isPrivate = false;
|
|
249
256
|
let dependencyNodes = [];
|
|
257
|
+
const bundleDependencyValues = /* @__PURE__ */ new Map();
|
|
250
258
|
return {
|
|
259
|
+
"Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.type=JSONLiteral]:matches([key.value=bundleDependencies], [key.value=bundledDependencies])"(node) {
|
|
260
|
+
bundleDependencyValues.set(node.key.value, node.value);
|
|
261
|
+
},
|
|
251
262
|
"Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.type=JSONLiteral][value.type=JSONLiteral][key.value=private]"(node) {
|
|
252
263
|
if (node.value.value === true) isPrivate = true;
|
|
253
264
|
},
|
|
@@ -256,7 +267,13 @@ const rule$15 = createRule({
|
|
|
256
267
|
},
|
|
257
268
|
"Program:exit"() {
|
|
258
269
|
if (ignorePrivate && isPrivate) return;
|
|
270
|
+
let bundleDependenciesValue = bundleDependencyValues.get("bundleDependencies");
|
|
271
|
+
if (!(bundleDependenciesValue && (bundleDependenciesValue.type !== "JSONLiteral" || Boolean(bundleDependenciesValue.value)))) bundleDependenciesValue = bundleDependencyValues.get("bundledDependencies");
|
|
272
|
+
if (bundleDependenciesValue?.type === "JSONLiteral" && bundleDependenciesValue.value === true) return;
|
|
273
|
+
const bundledDependencyNames = getBundledDependencyNames(bundleDependenciesValue);
|
|
259
274
|
for (const dependencyPropertyNode of dependencyNodes) {
|
|
275
|
+
const dependencyKey = dependencyPropertyNode.key;
|
|
276
|
+
if (isJSONStringLiteral(dependencyKey) && bundledDependencyNames.has(dependencyKey.value)) continue;
|
|
260
277
|
const dependencyValue = dependencyPropertyNode.value;
|
|
261
278
|
if (isJSONStringLiteral(dependencyValue) && isLocalDependency(dependencyValue.value)) context.report({
|
|
262
279
|
data: { name: dependencyValue.value },
|
|
@@ -845,10 +862,40 @@ const DEPENDENCY_TYPES = [
|
|
|
845
862
|
"peerDependencies"
|
|
846
863
|
];
|
|
847
864
|
const RANGE_TYPES = [
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
865
|
+
{
|
|
866
|
+
symbol: "^",
|
|
867
|
+
alias: "caret",
|
|
868
|
+
workspaceSymbol: "^"
|
|
869
|
+
},
|
|
870
|
+
{
|
|
871
|
+
alias: "pin",
|
|
872
|
+
workspaceSymbol: "*"
|
|
873
|
+
},
|
|
874
|
+
{
|
|
875
|
+
symbol: "~",
|
|
876
|
+
alias: "tilde",
|
|
877
|
+
workspaceSymbol: "~"
|
|
878
|
+
},
|
|
879
|
+
{
|
|
880
|
+
symbol: "<",
|
|
881
|
+
alias: "lt"
|
|
882
|
+
},
|
|
883
|
+
{
|
|
884
|
+
symbol: "<=",
|
|
885
|
+
alias: "le"
|
|
886
|
+
},
|
|
887
|
+
{
|
|
888
|
+
symbol: ">",
|
|
889
|
+
alias: "gt"
|
|
890
|
+
},
|
|
891
|
+
{
|
|
892
|
+
symbol: ">=",
|
|
893
|
+
alias: "ge"
|
|
894
|
+
}
|
|
851
895
|
];
|
|
896
|
+
const SYMBOLS = RANGE_TYPES.filter((rangeType) => "symbol" in rangeType).map((rangeType) => rangeType.symbol);
|
|
897
|
+
const RANGE_NAMES = RANGE_TYPES.map((rangeType) => rangeType.alias);
|
|
898
|
+
const SYMBOLS_AND_RANGE_NAMES = [...SYMBOLS, ...RANGE_NAMES];
|
|
852
899
|
const schemaOptions = {
|
|
853
900
|
additionalProperties: false,
|
|
854
901
|
properties: {
|
|
@@ -868,8 +915,8 @@ const schemaOptions = {
|
|
|
868
915
|
},
|
|
869
916
|
rangeType: {
|
|
870
917
|
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:
|
|
872
|
-
items: { enum:
|
|
918
|
+
oneOf: [{ enum: SYMBOLS_AND_RANGE_NAMES }, {
|
|
919
|
+
items: { enum: SYMBOLS_AND_RANGE_NAMES },
|
|
873
920
|
type: "array"
|
|
874
921
|
}]
|
|
875
922
|
}
|
|
@@ -877,33 +924,37 @@ const schemaOptions = {
|
|
|
877
924
|
required: ["rangeType"],
|
|
878
925
|
type: "object"
|
|
879
926
|
};
|
|
880
|
-
const
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
927
|
+
const normalizeRangeType = (rangeTypeOrSymbol) => RANGE_TYPES.find((rangeType) => "symbol" in rangeType && rangeType.symbol === rangeTypeOrSymbol || rangeType.alias === rangeTypeOrSymbol);
|
|
928
|
+
/** @returns `undefined` if workspace versions are not supported for the specified {@link rangeType} */
|
|
929
|
+
const getWorkspaceVersionForRange = (rangeType) => {
|
|
930
|
+
if ("workspaceSymbol" in rangeType) return `workspace:${rangeType.workspaceSymbol}`;
|
|
884
931
|
};
|
|
932
|
+
/** For displaying a range type in a user-facing way (ie. an error message). */
|
|
933
|
+
const displayRangeType = (rangeType) => "symbol" in rangeType ? rangeType.symbol : rangeType.alias;
|
|
934
|
+
const ROLLING_WORKSPACE_REGEX = /^workspace:[~^*]$/;
|
|
935
|
+
const isRollingWorkspaceSpec = (version) => ROLLING_WORKSPACE_REGEX.test(version);
|
|
936
|
+
const CHANGE_VERSION_RANGE_REGEX = /^(workspace:)?(\^|~|<=?|>=?)?/;
|
|
885
937
|
/**
|
|
886
938
|
* Given the original version, update it to use the correct range type.
|
|
887
939
|
*/
|
|
888
940
|
const changeVersionRange = (version, rangeType) => {
|
|
889
|
-
if (
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
default: return "workspace:~";
|
|
941
|
+
if (isRollingWorkspaceSpec(version)) {
|
|
942
|
+
const result = getWorkspaceVersionForRange(rangeType);
|
|
943
|
+
if (result !== void 0) return result;
|
|
893
944
|
}
|
|
894
|
-
|
|
945
|
+
const replaceWith = "symbol" in rangeType ? rangeType.symbol : "";
|
|
946
|
+
return version.replace(CHANGE_VERSION_RANGE_REGEX, `$1${replaceWith}`);
|
|
895
947
|
};
|
|
948
|
+
const IS_VERSION_SUPPORTED_REGEX = /^workspace:/;
|
|
896
949
|
/**
|
|
897
950
|
* Check if the version is in a form that this rule supports.
|
|
898
951
|
*/
|
|
899
952
|
const isVersionSupported = (version) => {
|
|
900
|
-
if (
|
|
901
|
-
const rawVersion = version.replace(
|
|
953
|
+
if (isRollingWorkspaceSpec(version)) return true;
|
|
954
|
+
const rawVersion = version.replace(IS_VERSION_SUPPORTED_REGEX, "");
|
|
902
955
|
return !!semver.validRange(rawVersion);
|
|
903
956
|
};
|
|
904
|
-
const
|
|
905
|
-
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
906
|
-
};
|
|
957
|
+
const POSSIBLE_WORKSPACE_SPEC_REGEX = /(?:workspace:)?[^~]?/;
|
|
907
958
|
const rule$9 = createRule({
|
|
908
959
|
create(context) {
|
|
909
960
|
if (!context.options[0]) return {};
|
|
@@ -920,39 +971,41 @@ const rule$9 = createRule({
|
|
|
920
971
|
const name = property.key.value;
|
|
921
972
|
const version = property.value.value;
|
|
922
973
|
if (!isVersionSupported(version)) continue;
|
|
923
|
-
const
|
|
924
|
-
|
|
925
|
-
|
|
974
|
+
const doesRangeTypeMatch = (rangeType) => {
|
|
975
|
+
if ("symbol" in rangeType && semver.validRange(version)) return version.startsWith(rangeType.symbol);
|
|
976
|
+
if (rangeType.alias === "pin" && (!!semver.parse(version) || version === "workspace:*" || version === "workspace:")) return true;
|
|
977
|
+
if (version.startsWith("workspace:")) {
|
|
978
|
+
const workspaceVersion = getWorkspaceVersionForRange(rangeType);
|
|
979
|
+
return workspaceVersion === void 0 || version.startsWith(workspaceVersion);
|
|
980
|
+
}
|
|
981
|
+
return false;
|
|
982
|
+
};
|
|
926
983
|
for (const options of optionsArray) {
|
|
927
984
|
if (options.forDependencyTypes && !options.forDependencyTypes.includes(dependencyType)) continue;
|
|
928
985
|
if (options.forPackages) {
|
|
929
986
|
if (!options.forPackages.some((packageNameRegex) => packageNameRegex.test(name))) continue;
|
|
930
987
|
}
|
|
931
|
-
if (options.forVersions && (
|
|
932
|
-
const rangeTypes = options.rangeTypes;
|
|
988
|
+
if (options.forVersions && (isRollingWorkspaceSpec(version) || version !== "*" && !semver.satisfies(version.replace(POSSIBLE_WORKSPACE_SPEC_REGEX, ""), options.forVersions))) continue;
|
|
989
|
+
const rangeTypes = options.rangeTypes.map(normalizeRangeType);
|
|
990
|
+
const validRangeTypes = rangeTypes.map(displayRangeType).join(", ");
|
|
933
991
|
if (version === "*") {
|
|
934
992
|
context.report({
|
|
935
|
-
data: { rangeTypes:
|
|
993
|
+
data: { rangeTypes: validRangeTypes },
|
|
936
994
|
messageId: "wrongRangeType",
|
|
937
995
|
node: property.value
|
|
938
996
|
});
|
|
939
997
|
break;
|
|
940
998
|
}
|
|
941
|
-
if (!rangeTypes.some(
|
|
942
|
-
|
|
943
|
-
case "caret": return isCaretRange;
|
|
944
|
-
case "pin": return isPinned;
|
|
945
|
-
case "tilde": return isTildeRange;
|
|
946
|
-
}
|
|
947
|
-
})) context.report({
|
|
948
|
-
data: { rangeTypes: rangeTypes.join(", ") },
|
|
999
|
+
if (!rangeTypes.some(doesRangeTypeMatch)) context.report({
|
|
1000
|
+
data: { rangeTypes: validRangeTypes },
|
|
949
1001
|
messageId: "wrongRangeType",
|
|
950
1002
|
node: property.value,
|
|
951
1003
|
suggest: rangeTypes.map((rangeType) => ({
|
|
952
1004
|
fix(fixer) {
|
|
953
1005
|
return fixer.replaceText(property.value, `"${changeVersionRange(version, rangeType)}"`);
|
|
954
1006
|
},
|
|
955
|
-
messageId:
|
|
1007
|
+
messageId: rangeType.alias === "pin" ? "changeToPin" : "changeTo",
|
|
1008
|
+
data: rangeType.alias === "pin" ? void 0 : { rangeType: displayRangeType(rangeType) }
|
|
956
1009
|
}))
|
|
957
1010
|
});
|
|
958
1011
|
break;
|
|
@@ -965,9 +1018,8 @@ const rule$9 = createRule({
|
|
|
965
1018
|
docs: { description: "Restricts the range of dependencies to allow or disallow specific types of ranges." },
|
|
966
1019
|
hasSuggestions: true,
|
|
967
1020
|
messages: {
|
|
968
|
-
changeToCaret: "Change to use a caret range.",
|
|
969
1021
|
changeToPin: "Pin the version.",
|
|
970
|
-
|
|
1022
|
+
changeTo: "Change to use a {{rangeType}} range",
|
|
971
1023
|
wrongRangeType: "This dependency is using the wrong range type. Acceptable range type(s): {{rangeTypes}}"
|
|
972
1024
|
},
|
|
973
1025
|
schema: [{ oneOf: [schemaOptions, {
|
|
@@ -1099,23 +1151,26 @@ const rule$7 = createRule({
|
|
|
1099
1151
|
});
|
|
1100
1152
|
//#endregion
|
|
1101
1153
|
//#region src/rules/scripts-name-casing.ts
|
|
1102
|
-
const BUILT_IN_SCRIPTS_IN_CAMEL_CASE = /* @__PURE__ */ new Set(["prepublishOnly"]);
|
|
1154
|
+
const BUILT_IN_SCRIPTS_IN_CAMEL_CASE = /* @__PURE__ */ new Set(["prepublishOnly", "pnpm:devPreinstall"]);
|
|
1103
1155
|
const rule$6 = createRule({
|
|
1104
1156
|
create(context) {
|
|
1157
|
+
const { ignoreNames = [], ignorePatterns = [] } = context.options[0] ?? {};
|
|
1158
|
+
const ignoreRegexes = ignorePatterns.map((pattern) => new RegExp(pattern));
|
|
1159
|
+
const isIgnored = (name) => ignoreNames.includes(name) || ignoreRegexes.some((regex) => regex.test(name));
|
|
1105
1160
|
return { "Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.value=scripts]"(node) {
|
|
1106
1161
|
if (node.value.type === "JSONObjectExpression") for (const property of node.value.properties) {
|
|
1107
1162
|
const keyNode = property.key;
|
|
1108
1163
|
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({
|
|
1164
|
+
if (BUILT_IN_SCRIPTS_IN_CAMEL_CASE.has(key) || isIgnored(key)) continue;
|
|
1165
|
+
const kebabCaseKey = (key.startsWith(".") ? key.slice(1) : key).split(":").map((segment) => kebabCase(segment)).join(":");
|
|
1166
|
+
if (kebabCaseKey !== (key.startsWith(".") ? key.slice(1) : key)) context.report({
|
|
1112
1167
|
data: { property: key },
|
|
1113
1168
|
messageId: "invalidCase",
|
|
1114
1169
|
node: keyNode,
|
|
1115
1170
|
suggest: [{
|
|
1116
1171
|
data: { property: key },
|
|
1117
1172
|
fix: (fixer) => {
|
|
1118
|
-
return fixer.replaceText(keyNode, JSON.stringify(kebabCaseKey));
|
|
1173
|
+
return fixer.replaceText(keyNode, JSON.stringify(key.startsWith(".") ? `.${kebabCaseKey}` : kebabCaseKey));
|
|
1119
1174
|
},
|
|
1120
1175
|
messageId: "convertToKebabCase"
|
|
1121
1176
|
}]
|
|
@@ -1124,6 +1179,7 @@ const rule$6 = createRule({
|
|
|
1124
1179
|
} };
|
|
1125
1180
|
},
|
|
1126
1181
|
meta: {
|
|
1182
|
+
defaultOptions: [{}],
|
|
1127
1183
|
docs: {
|
|
1128
1184
|
category: "Stylistic",
|
|
1129
1185
|
description: "Enforce that names for `scripts` are in kebab case (optionally separated by colons)."
|
|
@@ -1133,7 +1189,22 @@ const rule$6 = createRule({
|
|
|
1133
1189
|
convertToKebabCase: "Convert {{ property }} to kebab case.",
|
|
1134
1190
|
invalidCase: "Script name {{ property }} should be in kebab case."
|
|
1135
1191
|
},
|
|
1136
|
-
schema: [
|
|
1192
|
+
schema: [{
|
|
1193
|
+
additionalProperties: false,
|
|
1194
|
+
properties: {
|
|
1195
|
+
ignoreNames: {
|
|
1196
|
+
items: { type: ["string"] },
|
|
1197
|
+
type: ["array"],
|
|
1198
|
+
description: "Specific script names to ignore."
|
|
1199
|
+
},
|
|
1200
|
+
ignorePatterns: {
|
|
1201
|
+
items: { type: ["string"] },
|
|
1202
|
+
type: ["array"],
|
|
1203
|
+
description: "Regex patterns for script names to ignore."
|
|
1204
|
+
}
|
|
1205
|
+
},
|
|
1206
|
+
type: "object"
|
|
1207
|
+
}],
|
|
1137
1208
|
type: "suggestion"
|
|
1138
1209
|
},
|
|
1139
1210
|
name: "scripts-name-casing"
|
|
@@ -1543,6 +1614,130 @@ const createSimpleValidPropertyRule = (propertyName, validationFunction, aliases
|
|
|
1543
1614
|
ruleName
|
|
1544
1615
|
};
|
|
1545
1616
|
};
|
|
1617
|
+
//#endregion
|
|
1618
|
+
//#region src/utils/packageManager/constants.ts
|
|
1619
|
+
const AGENTS = [
|
|
1620
|
+
"npm",
|
|
1621
|
+
"yarn",
|
|
1622
|
+
"pnpm",
|
|
1623
|
+
"bun",
|
|
1624
|
+
"deno",
|
|
1625
|
+
"nub",
|
|
1626
|
+
"aube"
|
|
1627
|
+
];
|
|
1628
|
+
const LOCK_FILES = {
|
|
1629
|
+
"aube-lock.yaml": "aube",
|
|
1630
|
+
"aube-workspace.yaml": "aube",
|
|
1631
|
+
"bun.lock": "bun",
|
|
1632
|
+
"bun.lockb": "bun",
|
|
1633
|
+
"deno.lock": "deno",
|
|
1634
|
+
"nub.lock": "nub",
|
|
1635
|
+
"pnpm-lock.yaml": "pnpm",
|
|
1636
|
+
"pnpm-workspace.yaml": "pnpm",
|
|
1637
|
+
"yarn.lock": "yarn",
|
|
1638
|
+
"package-lock.json": "npm",
|
|
1639
|
+
"npm-shrinkwrap.json": "npm"
|
|
1640
|
+
};
|
|
1641
|
+
//#endregion
|
|
1642
|
+
//#region src/utils/packageManager/detectPackageManager.ts
|
|
1643
|
+
let packageManagerCache;
|
|
1644
|
+
const userAgentRegex = /^(.+?)\/(\S+)?/;
|
|
1645
|
+
function getPackageManagerFromUserAgent() {
|
|
1646
|
+
const userAgentMatch = process.env.npm_config_user_agent?.match(userAgentRegex);
|
|
1647
|
+
if (userAgentMatch) {
|
|
1648
|
+
const name = userAgentMatch[1];
|
|
1649
|
+
const version = userAgentMatch[2];
|
|
1650
|
+
if (AGENTS.includes(name)) return {
|
|
1651
|
+
name,
|
|
1652
|
+
version
|
|
1653
|
+
};
|
|
1654
|
+
}
|
|
1655
|
+
return null;
|
|
1656
|
+
}
|
|
1657
|
+
function setPackageManagerCache(result) {
|
|
1658
|
+
packageManagerCache = result;
|
|
1659
|
+
return packageManagerCache;
|
|
1660
|
+
}
|
|
1661
|
+
function* lookup(cwd = process.cwd()) {
|
|
1662
|
+
let directory = path.resolve(cwd);
|
|
1663
|
+
const { root } = path.parse(directory);
|
|
1664
|
+
while (directory && directory !== root) {
|
|
1665
|
+
yield directory;
|
|
1666
|
+
directory = path.dirname(directory);
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
function parsePackageJson(filepath) {
|
|
1670
|
+
if (!filepath || !fs.existsSync(filepath)) return null;
|
|
1671
|
+
return handlePackageManager(filepath);
|
|
1672
|
+
}
|
|
1673
|
+
/**
|
|
1674
|
+
* Detects the package manager used in the project.
|
|
1675
|
+
* @returns The detected package manager or `null` if not found.
|
|
1676
|
+
*/
|
|
1677
|
+
function detectPackageManager() {
|
|
1678
|
+
if (packageManagerCache) return packageManagerCache;
|
|
1679
|
+
const cwd = process.cwd();
|
|
1680
|
+
const strategies = [
|
|
1681
|
+
"lockfile",
|
|
1682
|
+
"packageManager-field",
|
|
1683
|
+
"user-agent"
|
|
1684
|
+
];
|
|
1685
|
+
for (const directory of lookup(cwd)) for (const strategy of strategies) switch (strategy) {
|
|
1686
|
+
case "lockfile":
|
|
1687
|
+
if (fs.existsSync(path.join(directory, "rush.json"))) return setPackageManagerCache({ name: "pnpm" });
|
|
1688
|
+
for (const lock of Object.keys(LOCK_FILES)) if (fs.existsSync(path.join(directory, lock))) {
|
|
1689
|
+
const name = LOCK_FILES[lock];
|
|
1690
|
+
return setPackageManagerCache(parsePackageJson(path.join(directory, "package.json")) ?? { name });
|
|
1691
|
+
}
|
|
1692
|
+
break;
|
|
1693
|
+
case "packageManager-field": {
|
|
1694
|
+
const result = parsePackageJson(path.join(directory, "package.json"));
|
|
1695
|
+
if (result) return setPackageManagerCache(result);
|
|
1696
|
+
break;
|
|
1697
|
+
}
|
|
1698
|
+
case "user-agent": {
|
|
1699
|
+
const result = getPackageManagerFromUserAgent();
|
|
1700
|
+
if (result) return setPackageManagerCache(result);
|
|
1701
|
+
break;
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
return null;
|
|
1705
|
+
}
|
|
1706
|
+
function getNameAndVersion(pkg) {
|
|
1707
|
+
const normalizeVersion = (version) => version?.match(/\d+(?:\.\d+){0,2}/)?.[0] ?? version;
|
|
1708
|
+
if (typeof pkg.packageManager === "string") {
|
|
1709
|
+
const [name, version] = pkg.packageManager.replace(/^\^/, "").split("@", 2);
|
|
1710
|
+
return {
|
|
1711
|
+
name,
|
|
1712
|
+
version: normalizeVersion(version)
|
|
1713
|
+
};
|
|
1714
|
+
}
|
|
1715
|
+
if (typeof pkg.devEngines?.packageManager?.name === "string") return {
|
|
1716
|
+
name: pkg.devEngines.packageManager.name,
|
|
1717
|
+
version: normalizeVersion(pkg.devEngines.packageManager.version)
|
|
1718
|
+
};
|
|
1719
|
+
}
|
|
1720
|
+
function handlePackageManager(filepath) {
|
|
1721
|
+
try {
|
|
1722
|
+
const content = fs.readFileSync(filepath, "utf8");
|
|
1723
|
+
const nameAndVer = getNameAndVersion(JSON.parse(content));
|
|
1724
|
+
if (nameAndVer) return {
|
|
1725
|
+
name: nameAndVer.name,
|
|
1726
|
+
version: nameAndVer.version
|
|
1727
|
+
};
|
|
1728
|
+
} catch {}
|
|
1729
|
+
return null;
|
|
1730
|
+
}
|
|
1731
|
+
//#endregion
|
|
1732
|
+
//#region src/rules/valid-properties.ts
|
|
1733
|
+
const packageManagerAwareValidateDependencies = (value) => {
|
|
1734
|
+
const result = detectPackageManager();
|
|
1735
|
+
if (result?.name === "pnpm") {
|
|
1736
|
+
const version = result.version;
|
|
1737
|
+
if (!version || semver.gte(version, "11.1.0")) return validateDependencies(value, { allowNamedRegistries: true });
|
|
1738
|
+
}
|
|
1739
|
+
return validateDependencies(value);
|
|
1740
|
+
};
|
|
1546
1741
|
/** All basic valid- flavor rules */
|
|
1547
1742
|
const rules$1 = Object.fromEntries([
|
|
1548
1743
|
["bin", validateBin],
|
|
@@ -1556,8 +1751,8 @@ const rules$1 = Object.fromEntries([
|
|
|
1556
1751
|
["contributors", validateContributors],
|
|
1557
1752
|
["cpu", validateCpu],
|
|
1558
1753
|
["description", validateDescription],
|
|
1559
|
-
["dependencies",
|
|
1560
|
-
["devDependencies",
|
|
1754
|
+
["dependencies", packageManagerAwareValidateDependencies],
|
|
1755
|
+
["devDependencies", packageManagerAwareValidateDependencies],
|
|
1561
1756
|
["devEngines", validateDevEngines],
|
|
1562
1757
|
["directories", validateDirectories],
|
|
1563
1758
|
["engines", validateEngines],
|
|
@@ -1611,7 +1806,7 @@ const pathEndsWith = (parent, child) => {
|
|
|
1611
1806
|
let pathToCheck = "";
|
|
1612
1807
|
return segments.reverse().some((segment) => {
|
|
1613
1808
|
pathToCheck = path.join(segment, pathToCheck);
|
|
1614
|
-
|
|
1809
|
+
return pathToCheck === child;
|
|
1615
1810
|
});
|
|
1616
1811
|
};
|
|
1617
1812
|
let repoRootCache = null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-package-json",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.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": {
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"eslint-fix-utils": "~0.4.3",
|
|
52
52
|
"eslint-json-compat-utils": "^0.2.3",
|
|
53
53
|
"jsonc-eslint-parser": "^3.1.0",
|
|
54
|
-
"package-json-validator": "^1.
|
|
54
|
+
"package-json-validator": "^1.6.0",
|
|
55
55
|
"semver": "^7.7.3",
|
|
56
56
|
"sort-object-keys": "^2.0.0",
|
|
57
57
|
"sort-package-json": "^4.0.0"
|
|
@@ -67,28 +67,28 @@
|
|
|
67
67
|
"@eslint/markdown": "8.0.1",
|
|
68
68
|
"@ianvs/prettier-plugin-sort-imports": "4.7.1",
|
|
69
69
|
"@types/node": "24.13.0",
|
|
70
|
-
"@types/semver": "7.
|
|
70
|
+
"@types/semver": "7.8.0",
|
|
71
71
|
"@typescript/native": "npm:typescript@7.0.2",
|
|
72
72
|
"@vitest/coverage-v8": "4.1.8",
|
|
73
73
|
"@vitest/eslint-plugin": "1.6.20",
|
|
74
|
-
"astro": "7.
|
|
74
|
+
"astro": "7.2.0",
|
|
75
75
|
"astro-og-canvas": "0.13.0",
|
|
76
76
|
"canvaskit-wasm": "0.41.1",
|
|
77
77
|
"console-fail-test": "0.6.0",
|
|
78
|
-
"eslint": "10.
|
|
78
|
+
"eslint": "10.8.0",
|
|
79
79
|
"eslint-doc-generator": "3.7.0",
|
|
80
|
-
"eslint-plugin-eslint-plugin": "7.
|
|
81
|
-
"eslint-plugin-jsdoc": "63.
|
|
82
|
-
"eslint-plugin-jsonc": "3.
|
|
80
|
+
"eslint-plugin-eslint-plugin": "7.6.0",
|
|
81
|
+
"eslint-plugin-jsdoc": "63.3.0",
|
|
82
|
+
"eslint-plugin-jsonc": "3.4.0",
|
|
83
83
|
"eslint-plugin-n": "18.2.1",
|
|
84
84
|
"eslint-plugin-node-dependencies": "2.2.0",
|
|
85
85
|
"eslint-plugin-perfectionist": "5.10.0",
|
|
86
86
|
"eslint-plugin-regexp": "3.1.0",
|
|
87
|
-
"eslint-plugin-unicorn": "
|
|
88
|
-
"eslint-plugin-yml": "3.
|
|
87
|
+
"eslint-plugin-unicorn": "73.0.0",
|
|
88
|
+
"eslint-plugin-yml": "3.8.0",
|
|
89
89
|
"jiti": "2.7.0",
|
|
90
90
|
"json-schema-to-ts": "3.1.1",
|
|
91
|
-
"knip": "6.
|
|
91
|
+
"knip": "6.32.0",
|
|
92
92
|
"prettier": "3.9.0",
|
|
93
93
|
"prettier-plugin-curly": "0.4.1",
|
|
94
94
|
"prettier-plugin-packagejson": "3.0.2",
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
"starlight-auto-sidebar": "0.4.0",
|
|
100
100
|
"tsdown": "0.22.2",
|
|
101
101
|
"typescript": "npm:@typescript/typescript6@6.0.2",
|
|
102
|
-
"typescript-eslint": "8.
|
|
102
|
+
"typescript-eslint": "8.66.0",
|
|
103
103
|
"vitest": "4.1.8"
|
|
104
104
|
},
|
|
105
105
|
"peerDependencies": {
|