Repository: JoshuaKGoldberg/eslint-plugin-package-json (ts)

Issue:
🚀 Feature: Bring in a valid-bundleDependencies rule
### Bug Report Checklist

- [x] I have tried restarting my IDE and the issue persists.
- [x] I have pulled the latest `main` branch of the repository.
- [x] I have [searched for related issues](https://github.com/JoshuaKGoldberg/eslint-plugin-package-json/issues?q=is%3Aissue) and found none that matched my issue.

### Overview

Following #42 -> #51: we've resolved to create a standalone `valid-bundleDependencies` rule. It should enforce that, _if_ the field exists in the `package.json` file, it's set to a valid value. It should do nothing if the field doesn't exist.

### Additional Info

Per discussion in #51, this won't be enabled by default at first. For this issue, we should:

1. Send a PR to create the rule in this repo (resolving this issue)
2. File an issue on https://github.com/JoshuaKGoldberg/package-json-validator to create a standalone API that performs the same validation
3. Once a version of `package-json-validator` is released with that feature, file an issue on this repo to switch the rule to use that API from `package-json-validator`

If you want to take on just step 1, that's ok - no pressure to do all three. 🙂 

Once _all_ `valid-` rules exist in this plugin, we can then file an issue to deprecate the `valid-package-def` rule in favor of them.

💖

Pull request:
feat(valid-bundleDependencies): add new rule for validating `bundleDependencies`
## PR Checklist

- [x] Addresses an existing open issue: fixes #819
- [x] That issue was marked as [`status: accepting prs`](https://github.com/JoshuaKGoldberg/eslint-plugin-package-json/issues?q=is%3Aopen+is%3Aissue+label%3A%22status%3A+accepting+prs%22)
- [x] Steps in [CONTRIBUTING.md](https://github.com/JoshuaKGoldberg/eslint-plugin-package-json/blob/main/.github/CONTRIBUTING.md) were taken

## Overview

This change adds a new `valid-bundleDependencies` rule that uses `validateBundleDependencies` from package-json-validator to identify errors.


Patch:
diff --git a/README.md b/README.md
index ec90a03..d50d185 100644
--- a/README.md
+++ b/README.md
@@ -139,6 +139,7 @@ The default settings don't conflict, and Prettier plugins can quickly fix up ord
 | [unique-dependencies](docs/rules/unique-dependencies.md)               | Checks a dependency isn't specified more than once (i.e. in `dependencies` and `devDependencies`) | ✔️ ✅ |    | 💡 |    |
 | [valid-author](docs/rules/valid-author.md)                             | Enforce that the author field is a valid npm author specification                                 | ✔️ ✅ |    |    |    |
 | [valid-bin](docs/rules/valid-bin.md)                                   | Enforce that the `bin` property is valid.                                                         | ✔️ ✅ |    | 💡 |    |
+| [valid-bundleDependencies](docs/rules/valid-bundleDependencies.md)     | Enforce that the `bundleDependencies` (or `bundledDependencies`) property is valid.               | ✔️ ✅ |    |    |    |
 | [valid-local-dependency](docs/rules/valid-local-dependency.md)         | Checks existence of local dependencies in the package.json                                        |      |    |    | ❌  |
 | [valid-name](docs/rules/valid-name.md)                                 | Enforce that package names are valid npm package names                                            | ✔️ ✅ |    |    |    |
 | [valid-package-definition](docs/rules/valid-package-definition.md)     | Enforce that package.json has all properties required by the npm spec                             | ✔️ ✅ |    |    |    |
diff --git a/docs/rules/valid-bundleDependencies.md b/docs/rules/valid-bundleDependencies.md
new file mode 100644
index 0000000..eb34411
--- /dev/null
+++ b/docs/rules/valid-bundleDependencies.md
@@ -0,0 +1,32 @@
+# valid-bundleDependencies
+
+💼 This rule is enabled in the following configs: ✔️ `legacy-recommended`, ✅ `recommended`.
+
+<!-- end auto-generated rule header -->
+
+This rule does the following checks on the value of the `bundleDependencies` property, as well as the its alias `bundledDependencies`:
+
+- It must be either an array or a boolean.
+- If the value is an array, it should only consist of non-empty strings
+
+Example of **incorrect** code for this rule:
+
+```json
+{
+	"bundleDependencies": [123]
+}
+```
+
+Example of **correct** code for this rule:
+
+```json
+{
+	"bundleDependencies": ["valid-dependency"]
+}
+```
+
+```json
+{
+	"bundleDependencies": true
+}
+```
diff --git a/src/plugin.ts b/src/plugin.ts
index f3c5e6a..5e06636 100644
--- a/src/plugin.ts
+++ b/src/plugin.ts
@@ -14,6 +14,7 @@ import { rule as sortCollections } from "./rules/sort-collections.js";
 import { rule as uniqueDependencies } from "./rules/unique-dependencies.js";
 import { rule as validAuthor } from "./rules/valid-author.js";
 import { rule as validBin } from "./rules/valid-bin.js";
+import { rule as validBundleDependencies } from "./rules/valid-bundleDependencies.js";
 import { rule as validLocalDependency } from "./rules/valid-local-dependency.js";
 import { rule as validName } from "./rules/valid-name.js";
 import { rule as validPackageDefinition } from "./rules/valid-package-definition.js";
@@ -40,6 +41,7 @@ const rules: Record<string, PackageJsonRuleModule> = {
 	"unique-dependencies": uniqueDependencies,
 	"valid-author": validAuthor,
 	"valid-bin": validBin,
+	"valid-bundleDependencies": validBundleDependencies,
 	"valid-local-dependency": validLocalDependency,
 	"valid-name": validName,
 	"valid-package-definition": validPackageDefinition,
diff --git a/src/rules/valid-bundleDependencies.ts b/src/rules/valid-bundleDependencies.ts
new file mode 100644
index 0000000..7b05fdd
--- /dev/null
+++ b/src/rules/valid-bundleDependencies.ts
@@ -0,0 +1,48 @@
+import { AST as JsonAST } from "jsonc-eslint-parser";
+import { validateBundleDependencies } from "package-json-validator";
+
+import { createRule } from "../createRule.js";
+import { formatErrors } from "../utils/formatErrors.js";
+
+export const rule = createRule({
+	create(context) {
+		const checkBundleDependencies = (node: JsonAST.JSONProperty) => {
+			const bundleDependenciesValueNode = node.value;
+			const bundleDependenciesValue: unknown = JSON.parse(
+				context.sourceCode.getText(bundleDependenciesValueNode),
+			);
+
+			const errors = validateBundleDependencies(bundleDependenciesValue);
+			if (errors.length) {
+				context.report({
+					data: {
+						errors: formatErrors(errors),
+						property: (node.key as JsonAST.JSONStringLiteral).value,
+					},
+					messageId: "validationError",
+					node: bundleDependenciesValueNode,
+				});
+			}
+		};
+		return {
+			"Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.value=bundledDependencies]":
+				checkBundleDependencies,
+			"Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.value=bundleDependencies]":
+				checkBundleDependencies,
+		};
+	},
+
+	meta: {
+		docs: {
+			category: "Best Practices",
+			description:
+				"Enforce that the `bundleDependencies` (or `bundledDependencies`) property is valid.",
+			recommended: true,
+		},
+		messages: {
+			validationError: "Invalid {{ property }}: {{ errors }}",
+		},
+		schema: [],
+		type: "problem",
+	},
+});


Test patch:
diff --git a/src/tests/rules/valid-bundleDependencies.test.ts b/src/tests/rules/valid-bundleDependencies.test.ts
new file mode 100644
index 0000000..0fd77c0
--- /dev/null
+++ b/src/tests/rules/valid-bundleDependencies.test.ts
@@ -0,0 +1,98 @@
+import { rule } from "../../rules/valid-bundleDependencies.js";
+import { ruleTester } from "./ruleTester.js";
+
+ruleTester.run("valid-bundleDependencies", rule, {
+	invalid: ["bundleDependencies", "bundledDependencies"]
+		.map((property) => [
+			{
+				code: `{
+	"${property}": null
+}
+`,
+				errors: [
+					{
+						data: {
+							errors: "the field is `null`, but should be an `Array` or a `boolean`",
+							property,
+						},
+						messageId: "validationError",
+					},
+				],
+			},
+			{
+				code: `{
+	"${property}": 123
+}
+`,
+				errors: [
+					{
+						data: {
+							errors: "the type should be `Array` or `boolean`, not `number`",
+							property,
+						},
+						messageId: "validationError",
+					},
+				],
+			},
+			{
+				code: `{
+	"${property}": "invalid"
+}
+`,
+				errors: [
+					{
+						data: {
+							errors: "the type should be `Array` or `boolean`, not `string`",
+							property,
+						},
+						messageId: "validationError",
+					},
+				],
+			},
+			{
+				code: `{
+	"${property}": { "invalid-bin": 123 }
+}
+`,
+				errors: [
+					{
+						data: {
+							errors: "the type should be `Array` or `boolean`, not `object`",
+							property,
+						},
+						messageId: "validationError",
+					},
+				],
+			},
+			{
+				code: `{
+	"${property}": ["valid", "", 123, null]
+}
+`,
+				errors: [
+					{
+						data: {
+							errors: `
+ - item at index 1 is empty, but should be a dependency name
+ - item at index 2 should be a string, not \`number\`
+ - item at index 3 should be a string, not \`null\``,
+							property,
+						},
+						messageId: "validationError",
+					},
+				],
+			},
+		])
+		.flat(),
+	valid: [
+		"{}",
+		...["bundleDependencies", "bundledDependencies"]
+			.map((property) => [
+				`{ "${property}": true }`,
+				`{ "${property}": false }`,
+				`{ "${property}": [] }`,
+				`{ "${property}": ["nin", "silver-mt-zion"] }`,
+			])
+			.flat(),
+	],
+});