Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create eslint plugin #709

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .changeset/slimy-fans-relate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
"eslint-plugin-ember-resources": major
---

Inpsired by this [Issue Report](https://github.com/NullVoxPopuli/ember-resources/issues/707),
thanks, [@swastik](https://github.com/swastik)!!

There is now an ESLint plugin to help with usage of resources to help prevent common footguns.

- `no-this-property-assignment-in-tracked-function`
prevents assigning properties on `this` when a `trackedFunction` is embedded within another class.
- `no-this-property-assignment-in-function-resource`
same as the above but for `resource({ })`
- `no-this-property-assignment-in-resource-thunk`
in the same spirit of https://github.com/ember-cli/eslint-plugin-ember/blob/master/docs/rules/no-side-effects.md

To Setup this plugin, install it, and then setup your `.eslintrc.js`:

```javascript
// .eslintrc.js
module.exports = {
// ...
plugins: ["ember-resources"],
extends: ["plugin:ember-resources/recommended"],
// ...
};
```
2 changes: 1 addition & 1 deletion .github/renovate.json5
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
},
{
// ensure maximum compatibility, when possible
"matchPaths": ["ember-resources/package.json"],
"matchPaths": ["ember-resources/package.json", "eslint-plugin/package.json"],
"matchDepTypes": ["dependencies"],
"enabled": false
},
Expand Down
5 changes: 5 additions & 0 deletions eslint-plugin/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use strict';

const { configs } = require('@nullvoxpopuli/eslint-configs');

module.exports = configs.nodeCJS();
9 changes: 9 additions & 0 deletions eslint-plugin/.prettierrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
'use strict';

module.exports = {
printWidth: 100,
semi: true,
singleQuote: true,
trailingComma: 'es5',
endOfLine: 'auto',
};
102 changes: 102 additions & 0 deletions eslint-plugin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# eslint-plugin-ember-resources

[![NPM version](https://img.shields.io/npm/v/eslint-plugin-ember-resources.svg?style=flat)](https://npmjs.org/package/eslint-plugin-ember-resources)
[![NPM downloads](https://img.shields.io/npm/dm/eslint-plugin-ember-resources.svg?style=flat)](https://npmjs.org/package/eslint-plugin-ember-resources)

> An ESlint plugin that provides set of rules enforcing consistent decorator positions

## ❗️Requirements

- [ESLint](https://eslint.org/) `>= 7`
- [Node.js](https://nodejs.org/) `>= 16`

## 🚀 Usage

### 1. Install plugin

```shell
yarn add --dev eslint-plugin-ember-resources
```

Or

```shell
npm install --save-dev eslint-plugin-ember-resources
```

### 2. Modify your `.eslintrc.js`

```javascript
// .eslintrc.js
module.exports = {
parser: '@babel/eslint-parser',
// parser: '@typescript-eslint/parser',
plugins: ['ember-resources'],
extends: [
'plugin:ember-resources/ember' // or other configuration
],
rules: {
// override rule settings from extends config here
// 'ember-resources/ember-resources': ['error', { /* your config */ }]
}
};
```

## 🧰 Configurations

| | Name | Description |
|:---|:-----|:------------|
| | [base](./src/config/base.js) | contains no rules settings, but the basic eslint configuration suitable for any project. You can use it to configure rules as you wish. |
| :hamster: | [ember](./src/config/ember.js) | extends the `base` configuration by enabling the recommended rules for ember projects. |

## 🍟 Rules

Rules are grouped by category to help you understand their purpose. Each rule has emojis denoting:

- What configuration it belongs to
- :wrench: if some problems reported by the rule are automatically fixable by the `--fix` [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) option

<!--RULES_TABLE_START-->

### side-effects

| | Rule ID | Description |
|:---|:--------|:------------|
| :white_check_mark: | [no-this-property-assignment-in-function-resource](./docs/rules/no-this-property-assignment-in-function-resource.md) | (no description) |
| :white_check_mark: | [no-this-property-assignment-in-resource-thunk](./docs/rules/no-this-property-assignment-in-resource-thunk.md) | (no description) |
| :white_check_mark: | [no-this-property-assignment-in-tracked-function](./docs/rules/no-this-property-assignment-in-tracked-function.md) | (no description) |

<!--RULES_TABLE_END-->

For the simplified list of rules, [go here](./src/index.js).

## 🍻 Contribution Guide

If you have any suggestions, ideas, or problems, feel free to [create an issue](https://github.com/NullVoxPopuli/eslint-plugin-ember-resources/issues/new), but first please make sure your question does not repeat [previous ones](https://github.com/NullVoxPopuli/eslint-plugin-ember-resources/issues).

### Creating a New Rule

- [Create an issue](https://github.com/NullVoxPopuli/eslint-plugin-ember-resources/issues/new) with a description of the proposed rule
- Create files for the [new rule](https://eslint.org/docs/developer-guide/working-with-rules):
- `src/rules/new-rule.js` (implementation, see [no-proxies](src/rules/no-proxies.js) for an example)
- `docs/rules/new-rule.md` (documentation, start from the template -- [raw](https://raw.githubusercontent.com/NullVoxPopuli/eslint-plugin-ember-resources/master/docs/rules/_TEMPLATE_.md), [rendered](docs/rules/_TEMPLATE_.md))
- `tests/src/rules/new-rule.js` (tests, see [no-proxies](tests/src/rules/no-proxies.js) for an example)
- Run `yarn update` to automatically update the README and other files (and re-run this if you change the rule name or description)
- Make sure your changes will pass [CI](.travis.yml) by running:
- `yarn test`
- `yarn lint` (`yarn lint:js --fix` can fix many errors)
- Create a PR and link the created issue in the description

Note that new rules should not immediately be added to the [recommended](./src/recommended-rules.js) configuration, as we only consider such breaking changes during major version updates.

## SemVer Policy

How does this project interpret patch / minor / breaking changes?

- **patch**: a change that fixes currently broken behavior. Does not cause formatting to change when people update unless a previous patch/feature accidentally broke formatting in a **breaking** way.
- **minor**: a change that does not impact formatting
- **breaking**: a major change that is not backwards compatible and would intentionally cause formatting changes to occur

## 🔓 License

See the [LICENSE](LICENSE.md) file for license rights and limitations (MIT).
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# no-this-property-assignment-in-function-resource

## Rule Details

## Examples

## Configuration
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# no-this-property-assignment-in-resource-thunk

## Rule Details

## Examples

## Configuration
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# no-this-property-assignment-in-tracked-function

## Rule Details

## Examples

## Configuration
48 changes: 48 additions & 0 deletions eslint-plugin/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"name": "eslint-plugin-ember-resources",
"version": "0.0.0",
"keywords": [
"eslint",
"ember",
"ember.js",
"plugin",
"styleguide",
"rules",
"resources"
],
"description": "plugin and additional rules for ESLint to help guide usage with ember-resources",
"repository": "https://github.com/NullVoxPopuli/ember-resources",
"license": "MIT",
"author": "NullVoxPopuli",
"files": [
"src"
],
"scripts": {
"test": "vitest --coverage",
"lint:js": "eslint . --cache",
"update": "node ./scripts/update-rules.js"
},
"devDependencies": {
"@babel/core": "^7.20.5",
"@babel/eslint-parser": "^7.19.1",
"@nullvoxpopuli/eslint-configs": "^2.1.1",
"@vitest/coverage-c8": "^0.25.8",
"eslint": "^7.32.0",
"prettier": "^2.7.1",
"typescript": "^4.9.4",
"vite": "^4.0.1",
"vitest": "^0.25.8"
},
"publishConfig": {
"registry": "https://registry.npmjs.org"
},
"engines": {
"node": "^16 || ^18"
},
"volta": {
"extends": "../package.json"
},
"peerDependencies": {
"eslint": "^7.32.0"
}
}
101 changes: 101 additions & 0 deletions eslint-plugin/scripts/update-rules.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/**
* This is a modified file that originally was created by:
* @author Toru Nagashima
* @copyright 2017 Toru Nagashima. All rights reserved.
* See LICENSE file in root directory for full license.
*/

'use strict';

// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------

const fs = require('fs');
const path = require('path');

// ------------------------------------------------------------------------------
// Main
// ------------------------------------------------------------------------------

const root = path.resolve(__dirname, '../src/rules');
const readmeFile = path.resolve(__dirname, '../README.md');
const tablePlaceholder = /<!--RULES_TABLE_START-->[\S\s]*<!--RULES_TABLE_END-->/;
const readmeContent = fs.readFileSync(readmeFile, 'utf8');

const STAR = ':white_check_mark:';
const PEN = ':wrench:';
const OCTANE = ':car:';

const rules = fs
.readdirSync(root)
.filter((file) => path.extname(file) === '.js')
.map((file) => path.basename(file, '.js'))
.map((fileName) => [fileName, require(path.join(root, fileName))]); // eslint-disable-line import/no-dynamic-require

const categories = rules
.map((entry) => entry[1].meta.docs.category)
.reduce((arr, category) => {
if (!arr.includes(category)) {
arr.push(category);
}

return arr;
}, []);

let rulesTableContent = categories
.map(
(category) => `### ${category}

| | Rule ID | Description |
|:---|:--------|:------------|
${rules
.filter(([, rule]) => rule.meta.docs.category === category && !rule.meta.deprecated)
.map((entry) => {
const name = entry[0];
const meta = entry[1].meta;
const mark = `${meta.docs.recommended ? STAR : ''}${meta.docs.octane ? OCTANE : ''}${
meta.fixable ? PEN : ''
}`;
const link = `[${name}](./docs/rules/${name}.md)`;
const description = meta.docs.description || '(no description)';

return `| ${mark} | ${link} | ${description} |`;
})
.join('\n')}
`
)
.join('\n');

const deprecatedRules = rules.filter((entry) => entry[1].meta.deprecated);

if (deprecatedRules.length > 0) {
rulesTableContent += `
### Deprecated

> :warning: We're going to remove deprecated rules in the next major release. Please migrate to successor/new rules.

| Rule ID | Replaced by |
|:--------|:------------|
${deprecatedRules
.map((entry) => {
const name = entry[0];
const meta = entry[1].meta;
const link = `[${name}](./docs/rules/${name}.md)`;
const replacedBy =
(meta.docs.replacedBy || []).map((id) => `[${id}](./docs/rules/${id}.md)`).join(', ') ||
'(no replacement)';

return `| ${link} | ${replacedBy} |`;
})
.join('\n')}
`;
}

fs.writeFileSync(
readmeFile,
readmeContent.replace(
tablePlaceholder,
`<!--RULES_TABLE_START-->\n\n${rulesTableContent}\n<!--RULES_TABLE_END-->`
)
);
10 changes: 10 additions & 0 deletions eslint-plugin/src/config/recommended.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
'use strict';

module.exports = {
plugins: ['ember-resources'],
rules: {
'ember-resources/no-this-property-assignment-in-resource-thunk': ['error', {}],
'ember-resources/no-this-property-assignment-in-tracked-function': ['error', {}],
'ember-resources/no-this-property-assignment-in-function-resource': ['error', {}],
},
};
12 changes: 12 additions & 0 deletions eslint-plugin/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
'use strict';

module.exports = {
rules: {
'no-this-property-assignment-in-resource-thunk': require('./rules/no-this-property-assignment-in-resource-thunk.js'),
'no-this-property-assignment-in-tracked-function': require('./rules/no-this-property-assignment-in-tracked-function.js'),
'no-this-property-assignment-in-function-resource': require('./rules/no-this-property-assignment-in-function-resource.js'),
},
configs: {
recommended: require('./config/recommended'),
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'use strict';

/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
meta: {
docs: {
recommended: true,
category: 'side-effects',
},
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'use strict';

/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
meta: {
docs: {
recommended: true,
category: 'side-effects',
},
},
};
Loading