eslint-plugin-package-json 1.7.1 → 1.8.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.
package/README.md CHANGED
@@ -46,6 +46,7 @@ See [Getting Started](https://eslint-plugin-package-json.dev/getting-started) fo
46
46
  | [no-redundant-files](https://eslint-plugin-package-json.dev/rules/no-redundant-files) | Prevents adding unnecessary / redundant files. | ✅ | | 💡 |
47
47
  | [no-redundant-publishConfig](https://eslint-plugin-package-json.dev/rules/no-redundant-publishConfig) | Warns when publishConfig.access is used in unscoped packages. | ✅ | | 💡 |
48
48
  | [order-properties](https://eslint-plugin-package-json.dev/rules/order-properties) | Enforces that package properties are declared in a consistent order. | 🎨 | 🔧 | |
49
+ | [prefer-rolling-workspace-spec](https://eslint-plugin-package-json.dev/rules/prefer-rolling-workspace-spec) | Require that dependencies declared with workspace protocol use a rolling workspace spec instead of a specific semver range. | | | 💡 |
49
50
  | [repository-shorthand](https://eslint-plugin-package-json.dev/rules/repository-shorthand) | Enforce either object or shorthand declaration for repository. | ✅ | 🔧 | |
50
51
  | [require-attribution](https://eslint-plugin-package-json.dev/rules/require-attribution) | Ensures that proper attribution is included, requiring that either `author` or `contributors` is defined, and that if `contributors` is present, it should include at least one contributor. | ✅ | | 💡 |
51
52
  | [restrict-dependency-ranges](https://eslint-plugin-package-json.dev/rules/restrict-dependency-ranges) | Restricts the range of dependencies to allow or disallow specific types of ranges. | | | 💡 |
@@ -1,7 +1,6 @@
1
1
  import { AST, RuleListener } from "jsonc-eslint-parser";
2
2
  import { AST as AST$1, Rule, SourceCode } from "eslint";
3
3
  import * as ESTree from "estree";
4
-
5
4
  //#region node_modules/.pnpm/json-schema-to-ts@3.1.1/node_modules/json-schema-to-ts/lib/types/definitions/jsonSchema.d.ts
6
5
  declare const $JSONSchema: unique symbol;
7
6
  type $JSONSchema = typeof $JSONSchema;
@@ -92,7 +91,8 @@ interface PackageJsonRuleModule<Options extends unknown[] = unknown[], Schema ex
92
91
  meta: Omit<Rule.RuleMetaData, 'defaultOptions' | 'docs' | 'schema'> & {
93
92
  defaultOptions?: NoInfer<Options>;
94
93
  docs?: Rule.RuleMetaData['docs'] & {
95
- category?: string; /** An optional route segment to group this rule under */
94
+ category?: string;
95
+ /** An optional route segment to group this rule under */
96
96
  ruleGroup?: string;
97
97
  };
98
98
  schema?: Schema;
@@ -1,6 +1,5 @@
1
- import { n as PackageJsonRuleModule, r as JSONSchema, t as PackageJsonPluginSettings } from "../createRule-29pC9RoP.mjs";
2
- import { ESLint } from "eslint";
3
-
1
+ import { n as PackageJsonRuleModule, r as JSONSchema, t as PackageJsonPluginSettings } from "../createRule-CSNxd7i2.mjs";
2
+ import { ESLint as ESLint$1 } from "eslint";
4
3
  //#region src/experimental/plugin.d.ts
5
4
  declare const plugin: {
6
5
  configs: {
@@ -9,7 +8,7 @@ declare const plugin: {
9
8
  language: string;
10
9
  name: string;
11
10
  plugins: {
12
- readonly 'package-json': ESLint.Plugin;
11
+ readonly 'package-json': ESLint$1.Plugin;
13
12
  };
14
13
  rules: {
15
14
  [k: string]: "error";
@@ -20,7 +19,7 @@ declare const plugin: {
20
19
  language: string;
21
20
  name: string;
22
21
  plugins: {
23
- readonly 'package-json': ESLint.Plugin;
22
+ readonly 'package-json': ESLint$1.Plugin;
24
23
  };
25
24
  rules: {
26
25
  [k: string]: "error";
@@ -37,7 +36,7 @@ declare const plugin: {
37
36
  };
38
37
  //#endregion
39
38
  //#region src/experimental/index.d.ts
40
- declare const configs: {
39
+ export declare const configs: {
41
40
  recommended: {
42
41
  files: string[];
43
42
  language: string;
@@ -61,8 +60,8 @@ declare const configs: {
61
60
  };
62
61
  };
63
62
  };
64
- declare const rules: {
63
+ export declare const rules: {
65
64
  [k: string]: PackageJsonRuleModule<unknown[], JSONSchema[]>;
66
65
  };
67
66
  //#endregion
68
- export { type PackageJsonPluginSettings, configs, plugin as default, rules };
67
+ export { type PackageJsonPluginSettings, plugin as default };
@@ -1,4 +1,4 @@
1
- import { t as plugin$1 } from "../plugin-BG90Pf7y.mjs";
1
+ import { t as plugin$1 } from "../plugin-DlF-r5D9.mjs";
2
2
  import { toCompatRule } from "eslint-json-compat-utils";
3
3
  //#region src/experimental/plugin.ts
4
4
  const language = "json/json";
package/dist/index.d.mts CHANGED
@@ -1,7 +1,6 @@
1
- import { n as PackageJsonRuleModule, r as JSONSchema, t as PackageJsonPluginSettings } from "./createRule-29pC9RoP.mjs";
1
+ import { n as PackageJsonRuleModule, r as JSONSchema, t as PackageJsonPluginSettings } from "./createRule-CSNxd7i2.mjs";
2
2
  import * as parserJsonc from "jsonc-eslint-parser";
3
- import { ESLint } from "eslint";
4
-
3
+ import { ESLint as ESLint$1 } from "eslint";
5
4
  //#region src/plugin.d.ts
6
5
  declare const plugin: {
7
6
  configs: {
@@ -12,12 +11,13 @@ declare const plugin: {
12
11
  };
13
12
  name: string;
14
13
  plugins: {
15
- readonly 'package-json': ESLint.Plugin;
14
+ readonly 'package-json': ESLint$1.Plugin;
16
15
  };
17
16
  rules: {
18
17
  [k: string]: "error";
19
18
  };
20
- }; /** @deprecated use the recommended config instead. this config will be removed in a future major release */
19
+ };
20
+ /** @deprecated use the recommended config instead. this config will be removed in a future major release */
21
21
  'recommended-publishable': {
22
22
  files: string[];
23
23
  languageOptions: {
@@ -25,7 +25,7 @@ declare const plugin: {
25
25
  };
26
26
  name: string;
27
27
  plugins: {
28
- readonly 'package-json': ESLint.Plugin;
28
+ readonly 'package-json': ESLint$1.Plugin;
29
29
  };
30
30
  rules: {
31
31
  [x: string]: "error";
@@ -38,7 +38,7 @@ declare const plugin: {
38
38
  };
39
39
  name: string;
40
40
  plugins: {
41
- readonly 'package-json': ESLint.Plugin;
41
+ readonly 'package-json': ESLint$1.Plugin;
42
42
  };
43
43
  rules: {
44
44
  [k: string]: "error";
@@ -53,8 +53,8 @@ declare const plugin: {
53
53
  };
54
54
  //#endregion
55
55
  //#region src/index.d.ts
56
- declare const rules: Record<string, PackageJsonRuleModule<unknown[], JSONSchema[]>>;
57
- declare const configs: {
56
+ export declare const rules: Record<string, PackageJsonRuleModule<unknown[], JSONSchema[]>>;
57
+ export declare const configs: {
58
58
  recommended: {
59
59
  files: string[];
60
60
  languageOptions: {
@@ -96,4 +96,4 @@ declare const configs: {
96
96
  };
97
97
  };
98
98
  //#endregion
99
- export { type PackageJsonPluginSettings, configs, plugin as default, rules };
99
+ export { type PackageJsonPluginSettings, plugin as default };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as plugin } from "./plugin-BG90Pf7y.mjs";
1
+ import { t as plugin } from "./plugin-DlF-r5D9.mjs";
2
2
  //#region src/index.ts
3
3
  const rules = plugin.rules;
4
4
  const configs = plugin.configs;
@@ -6,14 +6,19 @@ import detectIndent from "detect-indent";
6
6
  import { detectNewlineGraceful } from "detect-newline";
7
7
  import sortObjectKeys from "sort-object-keys";
8
8
  import sortPackageJson, { sortOrder } from "sort-package-json";
9
- import { execFileSync } from "node:child_process";
10
9
  import semver from "semver";
10
+ import { execFileSync } from "node:child_process";
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
12
  import fs from "node:fs";
13
13
  import path from "node:path";
14
14
  import process from "node:process";
15
15
  import { sep } from "node:path/posix";
16
16
  import { findRootSync } from "@altano/repository-tools";
17
+ //#region src/utils/predicates/isJSONNullLiteral.ts
18
+ function isJSONNullLiteral(node) {
19
+ return node.type === "JSONLiteral" && node.value === null;
20
+ }
21
+ //#endregion
17
22
  //#region src/utils/predicates/isJSONStringLiteral.ts
18
23
  function isJSONStringLiteral(node) {
19
24
  return node.type === "JSONLiteral" && typeof node.value === "string";
@@ -50,7 +55,7 @@ function createRule(rule) {
50
55
  }
51
56
  //#endregion
52
57
  //#region src/rules/bin-name-casing.ts
53
- const rule$18 = createRule({
58
+ const rule$19 = createRule({
54
59
  create(context) {
55
60
  return { "Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.value=bin]"(node) {
56
61
  if (node.value.type === "JSONObjectExpression") for (const property of node.value.properties) {
@@ -92,11 +97,12 @@ function isImplicitFormat(node) {
92
97
  if (node.type === "JSONLiteral") return true;
93
98
  return node.properties.every((property) => !isJSONStringLiteral(property.key) || !property.key.value.startsWith("."));
94
99
  }
95
- const rule$17 = createRule({
100
+ const rule$18 = createRule({
96
101
  create(context) {
97
102
  const [{ prefer = "explicit" } = {}] = context.options;
98
103
  function validateForExplicit(node) {
99
104
  const { value } = node;
105
+ if (isJSONNullLiteral(value)) return;
100
106
  if (value.type !== "JSONLiteral" && value.type !== "JSONObjectExpression" || !isImplicitFormat(value)) return;
101
107
  context.report({
102
108
  fix(fixer) {
@@ -113,6 +119,7 @@ const rule$17 = createRule({
113
119
  if (value.type !== "JSONObjectExpression") return;
114
120
  if (value.properties.length !== 1 || !isJSONStringLiteral(value.properties[0].key) || value.properties[0].key.value !== ".") return;
115
121
  const dotProperty = value.properties[0];
122
+ if (isJSONNullLiteral(dotProperty.value)) return;
116
123
  context.report({
117
124
  fix(fixer) {
118
125
  const valueText = context.sourceCode.getText(dotProperty.value);
@@ -193,7 +200,7 @@ const getTopLevelProperty = (node) => {
193
200
  return n.type === "JSONProperty" ? n.key : void 0;
194
201
  };
195
202
  const defaultIgnoreProperties = ["files"];
196
- const rule$16 = createRule({
203
+ const rule$17 = createRule({
197
204
  create(context) {
198
205
  const ignoreProperties = new Set(context.options[0]?.ignoreProperties ?? defaultIgnoreProperties);
199
206
  return {
@@ -249,7 +256,7 @@ const getBundledDependencyNames = (value) => {
249
256
  if (value?.type === "JSONArrayExpression") for (const element of value.elements.filter(isNotNullish).filter(isJSONStringLiteral)) names.add(element.value);
250
257
  return names;
251
258
  };
252
- const rule$15 = createRule({
259
+ const rule$16 = createRule({
253
260
  create(context) {
254
261
  const ignorePrivate = context.options[0]?.ignorePrivate ?? true;
255
262
  let isPrivate = false;
@@ -320,7 +327,7 @@ const getCachedLocalFileRegex = (filename) => {
320
327
  return regex;
321
328
  }
322
329
  };
323
- const rule$14 = createRule({
330
+ const rule$15 = createRule({
324
331
  create(context) {
325
332
  const entryCache = {
326
333
  bin: [],
@@ -397,7 +404,7 @@ const rule$14 = createRule({
397
404
  });
398
405
  //#endregion
399
406
  //#region src/rules/no-redundant-publishConfig.ts
400
- const rule$13 = createRule({
407
+ const rule$14 = createRule({
401
408
  create(context) {
402
409
  let packageName;
403
410
  let publishConfigAccessProperty;
@@ -443,7 +450,7 @@ const rule$13 = createRule({
443
450
  });
444
451
  //#endregion
445
452
  //#region src/rules/order-properties.ts
446
- const rule$12 = createRule({
453
+ const rule$13 = createRule({
447
454
  create(context) {
448
455
  return { "Program:exit"() {
449
456
  const { ast, text } = context.sourceCode;
@@ -500,6 +507,73 @@ const rule$12 = createRule({
500
507
  name: "order-properties"
501
508
  });
502
509
  //#endregion
510
+ //#region src/rules/prefer-rolling-workspace-spec.ts
511
+ const rollingWorkspaceSpecRegex = /^workspace:[*^~]?$/;
512
+ const convertibleSpecRegex = /^(workspace:[~^])/;
513
+ const rule$12 = createRule({
514
+ create(context) {
515
+ const { ignoreDependencies = [], ignorePatterns = [] } = context.options[0] ?? {};
516
+ const ignoreRegexes = ignorePatterns.map((pattern) => new RegExp(pattern));
517
+ const isIgnored = (name) => ignoreDependencies.includes(name) || ignoreRegexes.some((regex) => regex.test(name));
518
+ return { "Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.type=JSONLiteral]:matches([key.value=dependencies], [key.value=devDependencies])"(node) {
519
+ if (node.value.type === "JSONObjectExpression") for (const property of node.value.properties) {
520
+ const key = property.key.value;
521
+ if (isIgnored(key)) continue;
522
+ const valueNode = property.value;
523
+ if (!isJSONStringLiteral(valueNode) || !valueNode.value.startsWith("workspace:")) continue;
524
+ const dependencySpec = valueNode.value;
525
+ if (!rollingWorkspaceSpecRegex.test(dependencySpec)) {
526
+ let conversion;
527
+ const convertibleMatch = convertibleSpecRegex.exec(dependencySpec);
528
+ const rawVersion = dependencySpec.replace("workspace:", "");
529
+ if (convertibleMatch) conversion = convertibleMatch[1];
530
+ else if (semver.parse(rawVersion)) conversion = "workspace:*";
531
+ context.report({
532
+ messageId: "nonRollingWorkspaceSpec",
533
+ node: valueNode,
534
+ suggest: conversion ? [{
535
+ fix: (fixer) => {
536
+ return fixer.replaceText(valueNode, JSON.stringify(conversion));
537
+ },
538
+ messageId: "convertToRolling"
539
+ }] : void 0
540
+ });
541
+ }
542
+ }
543
+ } };
544
+ },
545
+ meta: {
546
+ defaultOptions: [{}],
547
+ docs: {
548
+ category: "Best Practices",
549
+ description: "Require that dependencies declared with workspace protocol use a rolling workspace spec instead of a specific semver range."
550
+ },
551
+ hasSuggestions: true,
552
+ messages: {
553
+ convertToRolling: "Convert to rolling workspace spec",
554
+ nonRollingWorkspaceSpec: "Workspace spec should not include specific semver range. Use a rolling workspace spec instead."
555
+ },
556
+ schema: [{
557
+ additionalProperties: false,
558
+ properties: {
559
+ ignoreDependencies: {
560
+ items: { type: ["string"] },
561
+ type: ["array"],
562
+ description: "Specific dependencies to ignore."
563
+ },
564
+ ignorePatterns: {
565
+ items: { type: ["string"] },
566
+ type: ["array"],
567
+ description: "Regex patterns for dependency names to ignore."
568
+ }
569
+ },
570
+ type: "object"
571
+ }],
572
+ type: "suggestion"
573
+ },
574
+ name: "prefer-rolling-workspace-spec"
575
+ });
576
+ //#endregion
503
577
  //#region src/utils/findPropertyWithKeyValue.ts
504
578
  function findPropertyWithKeyValue(properties, value) {
505
579
  return properties.find((property) => property.key.type === "JSONLiteral" && property.key.value === value);
@@ -1239,7 +1313,8 @@ const rule$5 = createRule({
1239
1313
  let naturalCompare;
1240
1314
  if (isScripts) {
1241
1315
  const scriptsSource = context.sourceCode.getText(node);
1242
- const { scripts: sortedScripts } = sortPackageJson(JSON.parse(`{${scriptsSource}}`));
1316
+ const minimalJson = JSON.parse(`{${scriptsSource}}`);
1317
+ const { scripts: sortedScripts } = sortPackageJson(minimalJson);
1243
1318
  const lifecycleIndex = new Map(Object.keys(sortedScripts).map((k, i) => [k, i]));
1244
1319
  naturalCompare = (a, b) => (lifecycleIndex.get(a) ?? 0) - (lifecycleIndex.get(b) ?? 0);
1245
1320
  } else naturalCompare = (a, b) => a > b ? 1 : -1;
@@ -1494,7 +1569,8 @@ const rule$2 = createRule({
1494
1569
  };
1495
1570
  return { "Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.value=author]"(node) {
1496
1571
  const valueNode = node.value;
1497
- const result = validateAuthor(JSON.parse(context.sourceCode.getText(valueNode)));
1572
+ const value = JSON.parse(context.sourceCode.getText(valueNode));
1573
+ const result = validateAuthor(value);
1498
1574
  reportIssues(result, valueNode, "author");
1499
1575
  } };
1500
1576
  },
@@ -1864,13 +1940,14 @@ const rule = createRule({
1864
1940
  //#region src/plugin.ts
1865
1941
  const require = createRequire(import.meta.url);
1866
1942
  const rules = {
1867
- "bin-name-casing": rule$18,
1868
- "exports-subpaths-style": rule$17,
1869
- "no-empty-fields": rule$16,
1870
- "no-local-dependencies": rule$15,
1871
- "no-redundant-files": rule$14,
1872
- "no-redundant-publishConfig": rule$13,
1873
- "order-properties": rule$12,
1943
+ "bin-name-casing": rule$19,
1944
+ "exports-subpaths-style": rule$18,
1945
+ "no-empty-fields": rule$17,
1946
+ "no-local-dependencies": rule$16,
1947
+ "no-redundant-files": rule$15,
1948
+ "no-redundant-publishConfig": rule$14,
1949
+ "order-properties": rule$13,
1950
+ "prefer-rolling-workspace-spec": rule$12,
1874
1951
  "require-attribution": rule$10,
1875
1952
  ...rules$2,
1876
1953
  "repository-shorthand": rule$11,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-package-json",
3
- "version": "1.7.1",
3
+ "version": "1.8.1",
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": {
@@ -57,50 +57,51 @@
57
57
  "sort-package-json": "^4.0.0"
58
58
  },
59
59
  "devDependencies": {
60
- "@astrojs/check": "0.9.9",
61
- "@astrojs/markdown-satteri": "0.3.3",
62
- "@astrojs/starlight": "0.41.3",
60
+ "@astrojs/check": "0.9.10",
61
+ "@astrojs/markdown-satteri": "0.4.1",
62
+ "@astrojs/starlight": "0.42.0",
63
63
  "@catppuccin/starlight": "2.1.0",
64
- "@eslint-community/eslint-plugin-eslint-comments": "4.7.2",
64
+ "@eslint-community/eslint-plugin-eslint-comments": "4.8.0",
65
65
  "@eslint/js": "10.0.1",
66
- "@eslint/json": "2.0.0",
67
- "@eslint/markdown": "8.0.1",
66
+ "@eslint/json": "2.1.0",
67
+ "@eslint/markdown": "8.0.3",
68
68
  "@ianvs/prettier-plugin-sort-imports": "4.7.1",
69
- "@types/node": "24.13.0",
69
+ "@types/node": "24.13.4",
70
70
  "@types/semver": "7.8.0",
71
71
  "@typescript/native": "npm:typescript@7.0.2",
72
- "@vitest/coverage-v8": "4.1.8",
73
- "@vitest/eslint-plugin": "1.6.20",
74
- "astro": "7.2.0",
75
- "astro-og-canvas": "0.13.0",
76
- "canvaskit-wasm": "0.41.1",
77
- "console-fail-test": "0.6.0",
78
- "eslint": "10.8.0",
79
- "eslint-doc-generator": "3.7.0",
80
- "eslint-plugin-eslint-plugin": "7.6.0",
81
- "eslint-plugin-jsdoc": "64.0.0",
82
- "eslint-plugin-jsonc": "3.4.0",
83
- "eslint-plugin-n": "18.2.1",
72
+ "@vitest/coverage-v8": "5.0.0",
73
+ "@vitest/eslint-plugin": "1.6.27",
74
+ "astro": "7.3.2",
75
+ "astro-og-canvas": "0.13.1",
76
+ "canvaskit-wasm": "0.42.0",
77
+ "console-fail-test": "0.6.1",
78
+ "eslint": "10.10.0",
79
+ "eslint-doc-generator": "3.7.1",
80
+ "eslint-plugin-eslint-plugin": "7.6.2",
81
+ "eslint-plugin-jsdoc": "64.3.8",
82
+ "eslint-plugin-jsonc": "3.4.2",
83
+ "eslint-plugin-n": "18.3.0",
84
84
  "eslint-plugin-node-dependencies": "2.2.0",
85
- "eslint-plugin-perfectionist": "5.10.0",
86
- "eslint-plugin-regexp": "3.1.0",
87
- "eslint-plugin-unicorn": "73.0.0",
88
- "eslint-plugin-yml": "3.8.0",
85
+ "eslint-plugin-perfectionist": "5.11.0",
86
+ "eslint-plugin-regexp": "3.3.0",
87
+ "eslint-plugin-unicorn": "74.0.0",
88
+ "eslint-plugin-yml": "3.8.1",
89
89
  "jiti": "2.7.0",
90
90
  "json-schema-to-ts": "3.1.1",
91
- "knip": "6.32.0",
92
- "prettier": "3.9.0",
91
+ "knip": "6.35.1",
92
+ "pkg-pr-new": "0.0.88",
93
+ "prettier": "3.9.6",
93
94
  "prettier-plugin-curly": "0.4.1",
94
95
  "prettier-plugin-packagejson": "3.0.2",
95
96
  "prettier-plugin-sh": "0.19.0",
96
97
  "pretty-quick": "4.2.2",
97
- "sharp": "0.35.0",
98
- "simple-git-hooks": "2.13.1",
99
- "starlight-auto-sidebar": "0.4.0",
100
- "tsdown": "0.22.2",
98
+ "sharp": "0.35.4",
99
+ "simple-git-hooks": "2.14.0",
100
+ "starlight-auto-sidebar": "0.4.1",
101
+ "tsdown": "0.23.0",
101
102
  "typescript": "npm:@typescript/typescript6@6.0.2",
102
- "typescript-eslint": "8.66.0",
103
- "vitest": "4.1.8"
103
+ "typescript-eslint": "8.70.0",
104
+ "vitest": "5.0.0"
104
105
  },
105
106
  "peerDependencies": {
106
107
  "@eslint/json": ">=1.0.0",