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

Chore: add generate new rule command #1645

Merged
merged 1 commit into from Oct 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 3 additions & 2 deletions docs/developer-guide/README.md
Expand Up @@ -13,8 +13,7 @@ Please include as much detail as possible to help us properly address your issue
In order to add a new rule or a rule change, you should:

- Create issue on GitHub with description of proposed rule
- Generate a new rule using the [official yeoman generator](https://github.com/eslint/generator-eslint)
- Run `npm start`
- Generate a new rule using the `npm run new -- [rule-name]` command
- Write test scenarios & implement logic
- Describe the rule in the generated `docs` file
- Make sure all tests are passing
Expand All @@ -38,10 +37,12 @@ After opening [astexplorer.net], select `Vue` as the syntax and `vue-eslint-pars
Since single file components in Vue are not plain JavaScript, we can't use the default parser, and we had to introduce additional one: `vue-eslint-parser`, that generates enhanced AST with nodes that represent specific parts of the template syntax, as well as what's inside the `<script>` tag.

To know more about certain nodes in produced ASTs, go here:

- [ESTree docs](https://github.com/estree/estree)
- [vue-eslint-parser AST docs](https://github.com/vuejs/vue-eslint-parser/blob/master/docs/ast.md)

The `vue-eslint-parser` provides few useful parser services, to help traverse the produced AST and access tokens of the template:

- `context.parserServices.defineTemplateBodyVisitor(visitor, scriptVisitor)`
- `context.parserServices.getTemplateBodyTokenStore()`

Expand Down
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -4,6 +4,7 @@
"description": "Official ESLint plugin for Vue.js",
"main": "lib/index.js",
"scripts": {
"new": "node tools/new-rule.js",
"start": "npm run test:base -- --watch --growl",
"test:base": "mocha \"tests/lib/**/*.js\" --reporter dot",
"test": "nyc npm run test:base -- \"tests/integrations/*.js\" --timeout 60000",
Expand Down
160 changes: 160 additions & 0 deletions tools/new-rule.js
@@ -0,0 +1,160 @@
const path = require('path')
const fs = require('fs')
const cp = require('child_process')
const logger = console

// main
;((ruleId) => {
if (ruleId == null) {
logger.error('Usage: npm run new <RuleID>')
process.exitCode = 1
return
}
if (!/^[\w-]+$/u.test(ruleId)) {
logger.error("Invalid RuleID '%s'.", ruleId)
process.exitCode = 1
return
}

const ruleFile = path.resolve(__dirname, `../lib/rules/${ruleId}.js`)
const testFile = path.resolve(__dirname, `../tests/lib/rules/${ruleId}.js`)
const docFile = path.resolve(__dirname, `../docs/rules/${ruleId}.md`)

fs.writeFileSync(
ruleFile,
`/**
* @author *****your name*****
* See LICENSE file in root directory for full license.
*/
'use strict'

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

// ...

// ------------------------------------------------------------------------------
// Helpers
// ------------------------------------------------------------------------------

// ...

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------

module.exports = {
meta: {
type: 'problem',
docs: {
description: '',
categories: undefined,
url: ''
},
fixable: null,
schema: [],
messages: {
// ...
}
},
/** @param {RuleContext} context */
create(context) {
// ...

return utils.defineTemplateBodyVisitor(context, {
// ...
})
}
}
`
)
fs.writeFileSync(
testFile,
`/**
* @author *****your name*****
* See LICENSE file in root directory for full license.
*/
'use strict'

const RuleTester = require('eslint').RuleTester
const rule = require('../../../lib/rules/${ruleId}')

const tester = new RuleTester({
parser: require.resolve('vue-eslint-parser'),
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module'
}
})

tester.run('${ruleId}', rule, {
valid: [
{
filename: 'test.vue',
code: \`
<template>

</template>
\`
},
],
invalid: [
{
filename: 'test.vue',
code: \`
<template>

</template>
\`,
errors: [
{
message: '...',
line: 'line',
column: 'col'
},
]
}
]
})
`
)
fs.writeFileSync(
docFile,
`---
pageClass: rule-details
sidebarDepth: 0
title: vue/${ruleId}
description: xxx
---
# vue/${ruleId}

> xxx

- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>

## :book: Rule Details

This rule ....

<eslint-code-block :rules="{'vue/${ruleId}': ['error']}">

\`\`\`vue
<template>

</template>
\`\`\`

</eslint-code-block>

## :wrench: Options

Nothing.

`
)

cp.execSync(`code "${ruleFile}"`)
cp.execSync(`code "${testFile}"`)
cp.execSync(`code "${docFile}"`)
})(process.argv[2])