Skip to content

Commit

Permalink
Chore: add generate new rule command (#1645)
Browse files Browse the repository at this point in the history
  • Loading branch information
ota-meshi authored Oct 2, 2021
1 parent c2c709d commit a56c7ec
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 2 deletions.
5 changes: 3 additions & 2 deletions docs/developer-guide/README.md
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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])

0 comments on commit a56c7ec

Please sign in to comment.