Skip to content

Commit

Permalink
🎉 Hello, World!
Browse files Browse the repository at this point in the history
  • Loading branch information
ynhhoJ committed Jan 28, 2023
1 parent f7f0260 commit d83da0e
Show file tree
Hide file tree
Showing 11 changed files with 3,283 additions and 0 deletions.
51 changes: 51 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"plugins": [
"@typescript-eslint"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"rules": {
"indent": [
"error",
2
],
"no-trailing-spaces": "error",
"semi": "error",
"keyword-spacing": "error",
"padding-line-between-statements": [
"error",
{
"blankLine": "always",
"prev": [
"const",
"let",
"var",
"if"
],
"next": "*"
},
{
"blankLine": "any",
"prev": [
"const",
"let",
"var"
],
"next": [
"const",
"let",
"var"
]
},
{
"blankLine": "always",
"prev": "*",
"next": "return"
}
]
},
}
34 changes: 34 additions & 0 deletions .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
# For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages

name: eslint-plugin-vue-required-attributes

on:
release:
types: [created]

jobs:
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- run: yarn install
- run: yarn test

publish-npm:
needs: tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
registry-url: https://registry.npmjs.org/
- run: yarn install
- run: yarn build
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
build
node_modules
109 changes: 109 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,111 @@
# eslint-plugin-vue-required-attributes

Inspired by [eslint-plugin-idiomatic-jsx](https://github.com/danrigsby/eslint-plugin-idiomatic-jsx) & `require-attributes` rule.

Require specified `attributes` on specified `components` from being used.

This is useful for things such as:

- Requiring a `id` attribute on things used by automated tests
- Requiring attributes needed for SEO or a11y concerns

## Installation

You'll first need to install ESLint:

```sh
# npm
npm install eslint --save-dev

# yarn
yarn add eslint --dev
```

Next, install `eslint-plugin-vue-required-attributes`:

```sh
# npm
npm install eslint-plugin-vue-required-attributes --save-dev

# yarn
yarn add eslint-plugin-vue-required-attributes --dev
```

## Configuration

Add `vue-required-attributes` to the plugins section of your `.eslintrc` configuration file. _You can omit the `eslint-plugin-` prefix_

```javascript
{
"plugins": [
"vue-required-attributes"
]
}
```

Configure the rules you want to use under the rules section.

```javascript
{
"rules": {
"vue-required-attributes/require-attributes": [ 1, {
// options
} ]
}
}
```

## Usage

This rule takes one object argument of type object that defines an associative array of `attributes` that that should be required on the defined array of `components`.

```json
{
"rules": {
"vue-required-attributes/require-attributes": [
1,
{
"id": ["a", "button", "input"]
}
]
}
}
```

### Succeed

```jsx
<a id='my-id'></a> <!-- Good: id is provided-->
<input id='my-id' /> <!-- Good: id is provided-->
```

### Fail

```jsx
<a></a> <!-- Bad: id is missing-->
<button></button> <!-- Bad: id is missing-->
```

### Custom output message

You may also pass in a 3rd option to change the default message that is output on error.

This can be handy if you want to explain "why" this rule is being used in your project or organization. This option is a `function` that takes in the `nodeType` and `attribute` name and returns a `string`.

```ts
{
"rules": {
"vue-required-attributes/require-attributes": [
1,
{
id: ['a', 'button'],
},
(componentName: string, missedAttribute: string) => `"${componentName}" missing "${missedAttribute} attribute."`
]
}
}
```

## ✏️ Code conduction

This project uses [Gitmoji](https://gitmoji.carloscuesta.me) for commit messages
6 changes: 6 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
verbose: false,
};
37 changes: 37 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "eslint-plugin-vue-required-attributes",
"version": "1.0.0",
"author": "ynhhoJ",
"description": "Require specified attributes on specified components from being used!",
"license": "MIT",
"main": "./build/index.js",
"files": [
"build"
],
"repository": {
"type": "git",
"url": "git+ssh://[email protected]:ynhhoJ/eslint-plugin-vue-required-attributes.git"
},
"scripts": {
"build": "tsc --build",
"test": "jest"
},
"devDependencies": {
"@types/eslint": "^8.4.10",
"@types/jest": "^29.4.0",
"@types/node": "^18.11.18",
"@typescript-eslint/eslint-plugin": "^5.49.0",
"@typescript-eslint/parser": "^5.49.0",
"eslint": "^8.32.0",
"jest": "^29.4.1",
"ts-jest": "^29.0.5",
"typescript": "^4.9.4",
"vue-eslint-parser": "^9.1.0"
},
"keywords": [
"eslint",
"vue",
"attributes",
"required"
]
}
7 changes: 7 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import requireAttributes from './rules/require-attributes';

export = {
rules: {
"require-attributes": requireAttributes,
},
};
64 changes: 64 additions & 0 deletions src/rules/require-attributes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { Rule } from "eslint";
import { AST } from "vue-eslint-parser";

const defaultMessage = (tagName: string, missedAttribute: string) => {
return `"${tagName}" component must have "${missedAttribute}" attribute.`;
};

const rule: Rule.RuleModule = {
meta: {
type: 'suggestion',
docs: {
description: 'Requires specified attribute to be used in component.',
recommended: true,
},
},

create: (context) => {
return context.parserServices.defineTemplateBodyVisitor({
VElement(node: AST.VElement) {
const options = context.options[0] || {};
const optionsObjectKeys = Object.keys(options);

if (!optionsObjectKeys.length) {
return;
}

const optionsSecond = context.options[1] || defaultMessage;
const elementName = node.rawName;

Object.keys(options).forEach((key) => {
const includesKey = options[key].includes(elementName);

if (!includesKey) {
return;
}

const elementAttributes = node.startTag.attributes;
const attributesName = elementAttributes.map((item) => {
const itemKey = item.key;

if (itemKey.type === 'VDirectiveKey' && "argument" in itemKey) {
const itemArgument = itemKey.argument as AST.VIdentifier;

return itemArgument.rawName;
}

return item.key.name;
});

if (attributesName.includes(key)) {
return;
}

context.report({
message: optionsSecond(elementName, key),
loc: node.loc,
});
});
}
});
},
};

export = rule;
Loading

0 comments on commit d83da0e

Please sign in to comment.