Skip to content

Commit

Permalink
feat(require-author): add new require-author rule
Browse files Browse the repository at this point in the history
This change adds the first or our new `require-` rules, and creates some foundational plumbing to make it super easy to add new require rules.  I've only done author in this PR.  Assuming this all looks good, we can quickly knock all the rest of the require rules in one shot.
  • Loading branch information
michaelfaith committed Feb 2, 2025
1 parent 336f1bf commit 81aecf0
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 1 deletion.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ The default settings don't conflict, and Prettier plugins can quickly fix up ord
| [no-redundant-files](docs/rules/no-redundant-files.md) | Prevents adding unnecessary / redundant files. | | | 💡 | |
| [order-properties](docs/rules/order-properties.md) | Package properties must be declared in standard order || 🔧 | | |
| [repository-shorthand](docs/rules/repository-shorthand.md) | Enforce either object or shorthand declaration for repository. || 🔧 | | |
| [require-author](docs/rules/require-author.md) | Requires the `author` property to be present. | | | | |
| [sort-collections](docs/rules/sort-collections.md) | Dependencies, scripts, and configuration values must be declared in alphabetical order. || 🔧 | | |
| [unique-dependencies](docs/rules/unique-dependencies.md) | Checks a dependency isn't specified more than once (i.e. in `dependencies` and `devDependencies`) || | 💡 | |
| [valid-local-dependency](docs/rules/valid-local-dependency.md) | Checks existence of local dependencies in the package.json || | | |
Expand Down
25 changes: 25 additions & 0 deletions docs/rules/require-author.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# require-author

<!-- end auto-generated rule header -->

This rule checks for the existence of the `"author"` property in a package.json,
and reports a violation if it doesn't exist.

Example of **incorrect** code for this rule:

```json
{
"name": "thee-silver-mt-zion",
"version": "13.0.0"
}
```

Example of **correct** code for this rule:

```json
{
"name": "thee-silver-mt-zion",
"version": "13.0.0",
"author": "Jessica Moss"
}
```
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"build": "tsup",
"format": "prettier \"**/*\" --ignore-unknown",
"lint": "eslint . --max-warnings 0",
"lint:eslint-docs": "npm run update:eslint-docs -- --check",
"lint:eslint-docs": "pnpm update:eslint-docs --check",
"lint:knip": "knip",
"lint:md": "markdownlint \"**/*.md\" \".github/**/*.md\"",
"lint:packages": "pnpm dedupe --check",
Expand Down
2 changes: 2 additions & 0 deletions src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { rule as noEmptyFields } from "./rules/no-empty-fields.js";
import { rule as noRedundantFiles } from "./rules/no-redundant-files.js";
import { rule as orderProperties } from "./rules/order-properties.js";
import { rule as preferRepositoryShorthand } from "./rules/repository-shorthand.js";
import { rules as requireRules } from "./rules/require-properties.js";
import { rule as sortCollections } from "./rules/sort-collections.js";
import { rule as uniqueDependencies } from "./rules/unique-dependencies.js";
import { rule as validLocalDependency } from "./rules/valid-local-dependency.js";
Expand All @@ -25,6 +26,7 @@ const rules: Record<string, PackageJsonRuleModule> = {
"no-empty-fields": noEmptyFields,
"no-redundant-files": noRedundantFiles,
"order-properties": orderProperties,
...requireRules,
"repository-shorthand": preferRepositoryShorthand,
"sort-collections": sortCollections,
"unique-dependencies": uniqueDependencies,
Expand Down
26 changes: 26 additions & 0 deletions src/rules/require-properties.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { PackageJsonRuleModule } from "../createRule.js";

import { createRequirePropertyRule } from "../utils/createRequirePropertyRule.js";

interface PropertyRule {
isRecommended: boolean;
propertyName: string;
}

// List of all properties we want to create require- rules for.
const properties = [
{ isRecommended: false, propertyName: "author" },
] satisfies PropertyRule[];

/** All require- flavor of rules */
const rules: Record<string, PackageJsonRuleModule> = {};

// Create all require- rules
for (const { isRecommended, propertyName } of properties) {
rules[`require-${propertyName}`] = createRequirePropertyRule(
propertyName,
isRecommended,
);
}

export { rules };
35 changes: 35 additions & 0 deletions src/tests/rules/require-author.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { rules } from "../../rules/require-properties.js";
import { ruleTester } from "./ruleTester.js";

ruleTester.run("require-author", rules["require-author"], {
invalid: [
{
code: "{}",
errors: [
{
data: { property: "author" },
line: 1,
messageId: "missing",
},
],
},
{
code: `{
"name": "foo",
"version": "1.0.0"
}
`,
errors: [
{
data: { property: "author" },
line: 1,
messageId: "missing",
},
],
},
],
valid: [
`{ "main": "./index.js", "author": "Sophie Trudeau" }`,
`{ "author": "Jessica Moss" }`,
],
});
42 changes: 42 additions & 0 deletions src/utils/createRequirePropertyRule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { createRule } from "../createRule.js";

/**
* Given a top-level property name, create a rule that requires that property to be present.
* Optionally, include it in the recommended config.
*/
export const createRequirePropertyRule = (
propertyName: string,
isRecommended = false,
) => {
return createRule({
create(context) {
let hasSeen = false;
return {
[`Program > JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.value=${propertyName}]`]:
() => {
hasSeen = true;
},
"Program:exit": () => {
if (!hasSeen) {
context.report({
data: { property: propertyName },
messageId: "missing",
node: context.sourceCode.ast,
});
}
},
};
},
meta: {
docs: {
description: `Requires the \`${propertyName}\` property to be present.`,
recommended: isRecommended,
},
messages: {
missing: "Property '{{property}}' is required.",
},
schema: [],
type: "suggestion",
},
});
};

0 comments on commit 81aecf0

Please sign in to comment.