Skip to content

Commit

Permalink
feat: add no-empty-fields rule
Browse files Browse the repository at this point in the history
  • Loading branch information
chouchouji committed Jan 18, 2025
1 parent 850cc2f commit 5a0d446
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { createRequire } from "node:module";

import type { PackageJsonRuleModule } from "./createRule.js";

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";
Expand All @@ -21,6 +22,7 @@ const { name, version } = require("../package.json") as {
};

const rules: Record<string, PackageJsonRuleModule> = {
"no-empty-fields": noEmptyFields,
"no-redundant-files": noRedundantFiles,
"order-properties": orderProperties,
"repository-shorthand": preferRepositoryShorthand,
Expand Down
93 changes: 93 additions & 0 deletions src/rules/no-empty-fields.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import type { AST as JsonAST } from "jsonc-eslint-parser";

import { createRule } from "../createRule";
import { isString } from "../utils/predicates";

export const rule = createRule({
create(context) {
const objectFields = [
"peerDependencies",
"scripts",
"dependencies",
"devDependencies",
];
const arrayFields = ["files"];

return {
"Program > JSONExpressionStatement > JSONObjectExpression"(
node: JsonAST.JSONObjectExpression,
) {
function getRange(
properties: JsonAST.JSONProperty[],
property: JsonAST.JSONProperty,
index: number,
): [number, number] {
const isLastProperty = properties.length - 1 === index;
// if the property is last, we should remove ',' before this property
const start = isLastProperty
? properties.slice(-2)[0].range[1]
: property.range[0];
// if the property isn't last, we should remove ',' after this property
const end = property.range[1] + (isLastProperty ? 0 : 1);
return [start, end];
}

node.properties.forEach((property, index) => {
if (
property.key.type === "JSONLiteral" &&
isString(property.key.value) &&
objectFields.includes(property.key.value) &&
property.value.type === "JSONObjectExpression" &&
!property.value.properties.length
) {
context.report({
data: {
property: property.key.value,
},
fix(fixer) {
return fixer.removeRange(
getRange(node.properties, property, index),
);
},
loc: property.loc,
messageId: "emptyFields",
});
} else if (
property.key.type === "JSONLiteral" &&
isString(property.key.value) &&
arrayFields.includes(property.key.value) &&
property.value.type === "JSONArrayExpression" &&
!property.value.elements.length
) {
context.report({
data: {
property: property.key.value,
},
fix(fixer) {
return fixer.removeRange(
getRange(node.properties, property, index),
);
},
loc: property.loc,
messageId: "emptyFields",
});
}
});
},
};
},
meta: {
docs: {
category: "Best Practices",
description: "Remove empty fields",
recommended: true,
},
hasSuggestions: true,
messages: {
emptyFields: 'Should remove empty "{{property}}"',
},
fixable: "whitespace",
schema: [],
type: "suggestion",
},
});
4 changes: 4 additions & 0 deletions src/utils/predicates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,7 @@ export function isNotNullish<T extends NonNullable<unknown>>(
): value is T {
return value !== null && value !== undefined;
}

export function isString(value: unknown): value is string {
return typeof value === "string";
}

0 comments on commit 5a0d446

Please sign in to comment.