Skip to content

Commit

Permalink
feat: add prefer-repository-shorthand rule
Browse files Browse the repository at this point in the history
  • Loading branch information
azat-io committed Nov 28, 2023
1 parent a72b702 commit 8facc1d
Show file tree
Hide file tree
Showing 8 changed files with 251 additions and 10 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.rulers": [80],
"eslint.experimental.useFlatConfig": false,
"eslint.probe": [
"javascript",
"javascriptreact",
Expand Down
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,13 @@ module.exports = {

🔧 Automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/user-guide/command-line-interface#--fix).

| Name                   | Description | 🔧 |
| :------------------------------------------------------------- | :-------------------------------------------------------------------------------------- | :- |
| [order-properties](docs/rules/order-properties.md) | Package properties must be declared in standard order | 🔧 |
| [sort-collections](docs/rules/sort-collections.md) | Dependencies, scripts, and configuration values must be declared in alphabetical order. | 🔧 |
| [valid-local-dependency](docs/rules/valid-local-dependency.md) | Checks existence of local dependencies in the package.json | |
| [valid-package-def](docs/rules/valid-package-def.md) | Enforce that package.json has all properties required by NPM spec | |
| Name                        | Description | 🔧 |
| :----------------------------------------------------------------------- | :-------------------------------------------------------------------------------------- | :- |
| [order-properties](docs/rules/order-properties.md) | Package properties must be declared in standard order | 🔧 |
| [prefer-repository-shorthand](docs/rules/prefer-repository-shorthand.md) | Enforce shorthand declaration for GitHub repository. | 🔧 |
| [sort-collections](docs/rules/sort-collections.md) | Dependencies, scripts, and configuration values must be declared in alphabetical order. | 🔧 |
| [valid-local-dependency](docs/rules/valid-local-dependency.md) | Checks existence of local dependencies in the package.json | |
| [valid-package-def](docs/rules/valid-package-def.md) | Enforce that package.json has all properties required by NPM spec | |

<!-- end auto-generated rules list -->
<!-- prettier-ignore-end -->
Expand Down
2 changes: 2 additions & 0 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
"quickstart",
"ruleset",
"tsup",
"vuejs",
"wontfix",
"yudai",
"zetlan",
"zetlen"
]
Expand Down
28 changes: 28 additions & 0 deletions docs/rules/prefer-repository-shorthand.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Enforce shorthand declaration for GitHub repository (`package-json/prefer-repository-shorthand`)

🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).

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

## Rule Details

This rule validates url for `repository` in a `package.json` and recommends a shortened version for GitHub repositories.

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

```json
{
"repository": {
"type": "git",
"url": "https://github.com/vuejs/core"
}
}
```

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

```json
{
"repository": "vuejs/core"
}
```
5 changes: 1 addition & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@
"bugs": {
"url": "https://github.com/zetlen/eslint-plugin-package-json/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/JoshuaKGoldberg/eslint-plugin-package-json"
},
"repository": "JoshuaKGoldberg/eslint-plugin-package-json",
"license": "MIT",
"author": {
"name": "James Zetlan",
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import orderProperties from "./rules/order-properties.js";
import preferRepositoryShorthand from "./rules/prefer-repository-shorthand.js";

Check warning on line 2 in src/index.ts

View check run for this annotation

Codecov / codecov/patch

src/index.ts#L2

Added line #L2 was not covered by tests
import sortCollections from "./rules/sort-collections.js";
import validLocalDependency from "./rules/valid-local-dependency.js";
import validPackageDef from "./rules/valid-package-def.js";

export const rules = {
"order-properties": orderProperties,
"prefer-repository-shorthand": preferRepositoryShorthand,

Check warning on line 9 in src/index.ts

View check run for this annotation

Codecov / codecov/patch

src/index.ts#L9

Added line #L9 was not covered by tests
"sort-collections": sortCollections,
"valid-local-dependency": validLocalDependency,
"valid-package-def": validPackageDef,
Expand Down
103 changes: 103 additions & 0 deletions src/rules/prefer-repository-shorthand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import type ESTree from "estree";
import type { JSONLiteral } from "jsonc-eslint-parser/lib/parser/ast.js";

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

const isGitHubUrl = (url: string) =>
/^(?:git\+)?(?:ssh:\/\/git@|http?s:\/\/)?(?:www\.)?github\.com\/.*\/.*/.test(
url,
);

const cleanGitHubUrl = (url: string) =>
url
.replace(
/^(?:git\+)?(?:ssh:\/\/git@|http?s:\/\/)?(?:www\.)?github\.com\//,
"",
)
.replace(/\.git$/, "");

export default createRule({
create(context) {
return {
JSONProperty: (node) => {
const message =
"Use shorthand repository URL for GitHub repository";
if (
node.key.type === "JSONLiteral" &&
node.key.value === "repository"
) {
if (node.value.type === "JSONObjectExpression") {
const { properties } = node.value;
const typeProperty = properties.find(
(property) =>
property.key.type === "JSONLiteral" &&
property.key.value === "type",
);
const urlProperty = properties.find(
(property) =>
property.key.type === "JSONLiteral" &&
property.key.value === "url",
);
const directoryProperty = properties.find(
(property) =>
property.key.type === "JSONLiteral" &&
property.key.value === "directory",
);
if (
!directoryProperty &&
typeProperty &&
typeProperty.value.type === "JSONLiteral" &&
typeProperty.value.value === "git" &&
urlProperty &&
urlProperty.value.type === "JSONLiteral" &&
typeof urlProperty.value.value === "string" &&
isGitHubUrl(urlProperty.value.value)
) {
context.report({
fix(fixer) {
return fixer.replaceText(
node.value as unknown as ESTree.Node,
JSON.stringify(
cleanGitHubUrl(
(
urlProperty.value as JSONLiteral
).value as string,
),
),
);
},
message,
node: node.value as unknown as ESTree.Node,
});
}
}

if (node.value.type === "JSONLiteral") {
const { value } = node.value;
if (typeof value === "string" && isGitHubUrl(value)) {
context.report({
fix(fixer) {
return fixer.replaceText(
node.value as unknown as ESTree.Node,
JSON.stringify(cleanGitHubUrl(value)),
);
},
message,
node: node.value as unknown as ESTree.Node,
});
}
}
}
},
};
},

meta: {
docs: {
category: "Best Practices",
description: "Enforce shorthand declaration for GitHub repository.",
recommended: true,
},
fixable: "code",
},
});
107 changes: 107 additions & 0 deletions src/tests/rules/prefer-repository-shorthand.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import rule from "../../rules/prefer-repository-shorthand.js";
import { ruleTester } from "./ruleTester.js";

ruleTester.run("prefer-repository-shorthand", rule, {
invalid: [
{
code: `{
"repository": {
"type": "git",
"url": "https://github.com/JoshuaKGoldberg/create-typescript-app"
}
}`,
errors: [
{
message:
"Use shorthand repository URL for GitHub repository",
type: "JSONObjectExpression",
},
],
filename: "package.json",
output: `{
"repository": "JoshuaKGoldberg/create-typescript-app"
}`,
},
{
code: `{
"repository": {
"type": "git",
"url": "git+https://github.com/vuejs/core.git"
}
}`,
errors: [
{
message:
"Use shorthand repository URL for GitHub repository",
type: "JSONObjectExpression",
},
],
filename: "package.json",
output: `{
"repository": "vuejs/core"
}`,
},
{
code: `{
"repository": {
"type": "git",
"url": "git+ssh://git@github.com/yudai-nkt/eslint-plugin-uvu.git"
}
}`,
errors: [
{
message:
"Use shorthand repository URL for GitHub repository",
type: "JSONObjectExpression",
},
],
filename: "package.json",
output: `{
"repository": "yudai-nkt/eslint-plugin-uvu"
}`,
},
{
code: `{
"repository": "https://github.com/eslint/eslint"
}`,
errors: [
{
message:
"Use shorthand repository URL for GitHub repository",
type: "JSONLiteral",
},
],
filename: "package.json",
output: `{
"repository": "eslint/eslint"
}`,
},
],
valid: [
{
code: `{
"repository": {
"type": "git",
"url": "https://github.com/facebook/react.git",
"directory": "packages/react"
}
}`,
filename: "package.json",
},
{
code: `{
"repository": "browserslist/browserslist"
}`,
filename: "package.json",
},
{
code: `{
"repository": {
"type": "git",
"url": "https://gitlab.com/gitlab/gitlab"
}
}`,
filename: "package.json",
},
],
});

0 comments on commit 8facc1d

Please sign in to comment.