# 开发指南
欢迎贡献。
# 🐛 错误报告
如果您认为在 ESLint 中发现了错误,请创建一个新问题 (opens new window)或 GitHub 上的拉取请求。
请提供尽可能多的详细信息,以帮助我们正确解决您的问题。 如果我们需要对问题进行分类并不断向人们询问更多细节,那就是实际解决问题的时间。 通过在您的问题中包含大量细节来帮助我们尽可能提高效率。
# ✨ 提出新规则或规则变更
为了添加新规则或规则更改,您应该:
- 在 GitHub 上创建带有提议规则描述的问题
- 使用 official yeoman generator (opens new window) 生成新规则
- 运行
npm start
- 编写测试场景和实现逻辑
- 在生成的
docs
文件中描述规则 - 确保所有测试都通过
- 运行
npm run lint
并修复所有错误 - 运行
npm run update
以更新自述文件和推荐配置 - 在描述中创建 PR 和链接创建的问题
我们很高兴看到潜在的贡献,所以不要犹豫。 如果您有任何建议、想法或问题,请随时添加新的 issue (opens new window),但首先请确保您的问题不重复之前的问题。
# 🔥 编写规则
在开始编写新规则之前,请阅读官方 ESLint 指南 (opens new window)。
接下来,为了了解您要检查的代码的 AST 是什么样的,请使用 [astexplorer.net]。 [astexplorer.net] 是检查 AST 的绝佳工具,暂未支持Mpx,仅可查看js相关
astexplorer.net (opens new window)
由于 Mpx 中的单个文件组件不是纯 JavaScript,我们不能使用默认解析器,我们不得不引入额外的一个:mpx-eslint-parser
,它生成增强的 AST,节点代表模板语法的特定部分,以及 <script>
标签内的内容。
要了解有关生成的 AST 中某些节点的更多信息,请访问此处:
Mpx-eslint-parser
提供了一些有用的解析器服务,以帮助遍历生成的 AST 和访问模板的令牌:
-context.parserServices.defineTemplateBodyVisitor(访问者,scriptVisitor)
-context.parserServices.getTemplateBodyTokenStore()
查看 示例规则 (opens new window) 以更好地了解这些规则是如何工作的。
请注意,关于您将在测试中编写什么样的代码示例,您必须在“RuleTester”中相应地设置解析器(不过,您可以根据每个测试用例进行设置)。 在此处查看示例 (opens new window)
最佳的编写规则实践是基于TDD(Test Drived Develop)的开发模式
- 在tests/lib下写下你的测试文件
const RuleTester = require('eslint').RuleTester
const rule = require('../../../lib/rules/test')
const tester = new RuleTester({
parser: require.resolve('mpx-eslint-parser'),
parserOptions: { ecmaVersion: 2015 }
})
tester.run('test', rule, {
valid: [
{
filename: 'test.mpx',
code: '<template><view class="a"><input class="b"></input><view class="c"></view><view class="d"></view></view></template>'
}
],
invalid: [
{
filename: 'test.mpx',
code: '<script>function a (){ this.b = 1 }</script>',
errors: ['wrong']
}
]
})
- 在lib/rules下写规则的详细,并且在lib/index下导出
const utils = require('../utils')
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'test file',
categories: [''],
url: 'https://mpx-ecology.github.io/eslint-plugin-mpx/rules/test.html'
},
fixable: null,
schema: []
},
/** @param {RuleContext} context */
create(context) {
return utils.defineTemplateBodyVisitor(context, {
/** @param {VDirective} node */
"VElement[name='input'] ~ VElement"(node) {
}
}, {
'MemberExpression > ThisExpression'(node) {}
})
}
}
- 通过npm run test进行调试
如果您遇到困难,请记住您已经可以学习很多规则,如果您找不到正确的解决方案 - 请不要犹豫,提出issue解决问题。我们很乐意提供帮助!
# 🔍 灵活匹配节点
我们在编写规则的时候,想要匹配某个节点下的子节点或者兄弟节点,可以通过esquery (opens new window)提供的方式来编写我们的visitor
/*
<template>
<view class="a">
<input class="b"></input>
<view class="c"></view>
<view class="d"></view>
</view>
</template>
*/
create(context) {
return utils.defineTemplateBodyVisitor(context, {
// 匹配属性为class的节点
"VAttribute[key.name='class']"(node) {},
// 匹配属性为class值为a的节点
"VAttribute[key.name='class'][value.value='a']"(node) {},
// 匹配tempalte节点下属性有class的所有节点
"VElement[name='template'] VAttribute[key.name='class'"(node) {},
// 匹配tempalte节点的children节点为VElement类型的 (即为class=a的节点)
"VElement[name='template'] > VElement"(node) {},
// 匹配input节点所有兄弟节点 (即为class=c的节点和class=d的节点)
"VElement[name='input'] ~ VElement"(node) {},
// 匹配input节点下一个兄弟节点 (即为class=c的节点)
"VElement[name='input'] + VElement"(node) {}
},
{
// js模块同理,这里不过多展示
// 匹配function中表达式的this表达式
'MemberExpression > ThisExpression'(node) {}
}
)
}
# ⚡️ 内置工具函数
# visitor相关
- defineTemplateBodyVisitor (定义模版Visitor)
- wrapCoreRule (Eslint规则适配Template)
- executeOnMpx (检测当前文件是否是mpx文件且是创建mpx方法)
- excuteOnMpxCreateApp (检测是否是在createApp函数并且执行callback)
- excuteOnMpxCreateComponent (检测是否是在createComponent函数并且执行callback)
- excuteOnMpxCreatePage (检测是否是在createPage函数并且执行callback)
- executeOnFunctionsWithoutReturn (查找所有没有返回值的函数)
- compositingVisitors (合并Visitors)
- defineMpxVisitor (定义处理程序以遍历 Mpx 对象)
# 获取
- prevSibling (获取给定元素的前一个兄弟元素)
- getAttribute (获取具有给定名称的属性)
- getDirective (获取具有给定名称的指令)
- getDirectives (获取具有给定名称的指令列表)
- getStaticPropertyName (获取给定节点的属性名称)
- getStringLiteralValue (获取给定节点的字符串)
- getComponentPropsFromOptions (通过查看所有组件的属性来获取所有道具)
- getComputedProperties (通过查看所有组件的属性来获取所有计算属性)
- getMpxObjectType (如果是在创建mpx实例,则返回内部对象)
- *iterateProperties (具有所有属性的返回generator)
- getMemberChaining (获取 MemberExpression 的链接节点)
- findProperty (从给定的 ObjectExpression 节点中查找具有给定名称的属性)
- findMutating (检查给定节点是否有改变值的表达式)
# 检测
- isDef (检查给定值是否已定义)
- hasAttribute (检查给定的开始标签是否有特定的属性)
- hasDirective (检查给定的开始标签是否有特定的指令)
- isEmptyValueDirective (检查给定的指令属性是否具有空值(
=""
)) - isEmptyExpressionValueDirective (检查给定的指令属性是否有它们的空表达式值(例如
=""
)) - prevElementHasIf (检查前一个兄弟元素是否有
if
或elif
指令) - isCustomComponent (检查给定节点是否是自定义组件)
- isMpElementName (检查给定名称是否是小程序元素)
- isMpxFile (检测文件是不是.mpx后缀的文件)
- isSingleLine (检测是否在同一行)
- hasInvalidEOF (检查程序的templateBody是否有无效的EOF)
- editDistance (返回两个字符串editdistanc)
- isProperty (检测节点是否为property节点)
- isVElement (检测节点是否为VElement节点)
- isThis (检查给定节点是
this
还是存储this
的变量) - equalTokens (检查两个给定节点的令牌是否相同)
# defineTemplateBodyVisitor
定义模版Visitor,匹配template和js的节点进行规则处理
/* @param context 解析器的上下文
* @param templateBodyVisitor 遍历模板的visitor.
* @param [scriptVisitor] 遍历script的visitor.
* @returns {RuleListener} 合并的visitor.
*/
function defineTemplateBodyVisitor(
context: RuleContext,
templateBodyVisitor: TemplateListener,
scriptVisitor?: RuleListener
): RuleListener
// example
create(context) {
let hasInvalidEOF = null
return defineTemplateBodyVisitor(context, {
"VAttribute[directive=true][key.name.name='if']"(node) {
// 检查程序的templateBody是否有无效的EOF,有的话不走了
if (hasInvalidEOF) return
// 检测wx:if和wx:else是否在同一个标签
const element = node.parent.parent
if (utils.hasDirective(element, 'else')) {
context.report({
node,
loc: node.loc,
message:
"'wx:if' and 'wx:else' directives can't exist on the same element. You may want 'wx:elif' directives."
})
}
}
}, {
'Program'(node) {
hasInvalidEOF = utils.hasInvalidEOF(node)
}
})
}
# wrapCoreRule
Eslint规则适配Template,可以让eslint规则检测tempalte中的表达式
/**
* @callback WrapCoreRuleCreate
* @param {RuleContext} ruleContext
* @param {WrapCoreRuleCreateContext} wrapContext
* @returns {TemplateListener}
*
* @typedef {object} WrapCoreRuleCreateContext
* @property {RuleListener} coreHandlers
*/
/**
* @callback WrapCoreRulePreprocess
* @param {RuleContext} ruleContext
* @param {WrapCoreRulePreprocessContext} wrapContext
* @returns {void}
*
* @typedef {object} WrapCoreRulePreprocessContext
* @property { (override: Partial<RuleContext>) => RuleContext } wrapContextToOverrideProperties 重写规则的context
* @property { (visitor: TemplateListener) => void } defineVisitor 定义template的Visitor
*/
/**
* 让eslint core里的规则适用于mpx文件
* @param {string} coreRuleName core里的规则名字
* @param {Object} [options] 规则配置项
* @param {string[]} [options.categories] 规则的类别
* @param {boolean} [options.skipDynamicArguments] 如果为true,则跳过动态参数
* @param {boolean} [options.skipDynamicArgumentsReport] 如果“true”,则跳过动态参数中的报告。
* @param {WrapCoreRulePreprocess} [options.preprocess] 调用核心规则创建的预处理。
* @param {WrapCoreRuleCreate} [options.create] 如果定义,则扩展核心规则。
* @returns {RuleModule} 包装的规则实现。
*/
function wrapCoreRule(coreRuleName: string, options: Object): RuleModule
// example
// eslint-disable-next-line
// 让eqeqeq在template中的属性中生效
// <view wx:if="{{ a === 1 }}"></view>
module.exports = wrapCoreRule('eqeqeq', {
applyDocument: true
})
# isDef
检查给定值是否已定义。
/**
* @template T
* @param {T | null | undefined} v
* @returns {v is T}
*/
function isDef<T>(v:T | null | undefined): T
// example
// node 不为null就返回true
// isDef(null) => false
// isDef(undefined) = > true
// isDef({ node }) => true
# prevSibling
获取给定元素的前一个兄弟元素。
/**
* @param {VElement} node 获取上一个兄弟元素的元素节点。
* @returns {VElement|null} 上一个兄弟元素。
*/
function prevSibling(node: VElement): VElement|null
// example
// <view>a</view><view>b</view>
// prevSibling(node) 如果node为b,则返回a
# hasAttribute
检查给定的开始标签是否有特定的属性。
/**
* @param {VElement} node 要检查的开始标记节点。
* @param {string} name 要检查的属性名称。
* @param {string} [value] 要检查的属性值。
* @returns {boolean} `true`代表开始标签具有属性。
*/
function hasAttribute(node:VElement, name:string, value?:string): boolean
// example
// <view class="item"></view>
hasAttribute(node, 'class', 'item') => true
# hasDirective
检查给定的开始标签是否有特定的指令。
/**
* @param {VElement} node 要检查的开始标记节点。
* @param {string} name 要检查的指令名称。
* @param {string} [argument] 要检查的指令参数。
* @returns {boolean} `true`代表开始标签具有指令。
*/
function hasDirective(node:VElement, name:string, argument?:string): boolean
// example
// <view wx:if="a"></view>
hasDirective(node, 'if') => true
# isEmptyValueDirective
检查给定的指令属性是否具有空值(
=""、="{{}}"
)。
/**
* @param {VDirective} node 要检查的指令属性节点。
* @param {RuleContext} context 使用解析器服务的规则上下文。
* @returns {boolean} `true` 代表指令属性的值为空(`=""`)。
*/
function isEmptyValueDirective(node:VDirective, context:RuleContext): boolean
// example
isEmptyValueDirective(node, context)
// wx:if="" => true
// wx:if="{{}}" => true
// wx:if="{{a}}" => false
# isEmptyExpressionValueDirective
检查给定的指令属性是否有它们的空表达式值(例如
=""
)。
/**
* @param {VDirective} node 要检查的指令属性节点。
* @param {RuleContext} context T使用解析器服务的规则上下文。
* @returns {boolean} `true` 如果指令属性的表达式值为空。
*/
function isEmptyExpressionValueDirective(node:VDirective, context:RuleContext): boolean
// example
isEmptyExpressionValueDirective(node, context)
// wx:if="" => true
// 方法待改进
# getAttribute
获取具有给定名称的属性。
/**
* @param {VElement} node 要检查的开始标记节点。
* @param {string} name 要检查的属性名称。
* @param {string} [value] 要检查的属性值。
* @returns {VAttribute | null} 找到的属性。
*/
function getAttribute(node:VElement, name:string, value:string):VAttribute|null {
// example
// <view class="item"></view>
getAttribute(node, 'class', 'item') => VAttribute
# getDirective
获取具有给定名称的指令。
/**
* @param {VElement} node 要检查的开始标记节点。
* @param {string} name 要检查的指令名称。
* @param {string} [value] 要检查的指令值。
* @returns {VAttribute | null} 找到的指令。
*/
function getDirective(node:VElement, name:string, value?:string):VAttribute|null {
// example
// <view wx:if="{{a}}">
getDirective(node, 'if', 'a') => VAttribute
# getDirectives
获取具有给定名称的指令列表。
/**
* @param {VElement | VStartTag} node 要检查的开始标记节点。
* @param {string} name 要检查的指令名称。
* @returns {VDirective[]} 指令的数组。
*/
function getDirectives(node:VElement| VStartTag, name:string):VDirective[] {
// example
// <view wx:if="{{a}}"><view wx:if="{{b}}"></view></view>
getDirectives(node, 'if')
// 返回所有if的节点
# prevElementHasIf
检查前一个兄弟元素是否有
if
或elif
指令。
/**
* @param {VElement} node 要检查的元素节点。
* @returns {boolean} 如果前一个兄弟元素有 `if` 或 `elif` 指令,则为 `true`。
*/
function prevElementHasIf(node:VElement): boolean
// example
prevElementHasIf(node)
// <view></view> => false
// <view wx:if="{{a}}"></view> => false
// <view wx:elif="{{b}}"></view> => true
# isCustomComponent
检查给定节点是否是自定义组件。
/**
* @param {VElement} node 要检查的开始标记节点。
* @returns {boolean} `true` 如果节点是自定义组件。
*/
function isCustomComponent(node: VElement): boolean
// example
isCustomComponent(node)
// <component is="a" /> => true
# isMpElementName
检查给定名称是否是小程序元素。
/**
* @param {string} name 要检查的名称。
* @returns {boolean} `true` 如果名称是小程序元素名称。
*/
function isMpElementName(name:string)
// example
isMpElementName('view') => true
isMpElementName('iframe') => false
# getStaticPropertyName
获取给定节点的属性名称。
/**
* @param {Property|AssignmentProperty|MethodDefinition|MemberExpression} node - 要获取的节点。
* @return {string|null} 属性名称(如果是静态的)。 否则为空。
*/
function getStaticPropertyName(node: Property|AssignmentProperty|MethodDefinition|MemberExpression): string|null
// example
<script>
createComponent({
computed: { // getStaticPropertyName(node) => computed
foo: function () {} // getStaticPropertyName(node) => foo
}
})
</script>
# getStringLiteralValue
获取给定节点的字符串。
/**
* @param {Literal|TemplateLiteral} node - 要获取的节点。
* @return {string|null} 如果是静态的,则为字符串。 否则为空。
*/
function getStringLiteralValue(node: Literal|TemplateLiteral): string|null
// example
<script>
function a() {
return true // getStringLiteralValue(node) true
? 1 // getStringLiteralValue(node) 1
: 2 // getStringLiteralValue(node) 2
}
</script>
# getComponentPropsFromOptions
通过查看所有组件的属性来获取所有道具
/**
* @param {ObjectExpression} componentObject 具有组件定义的对象
* @return {(ComponentArrayProp | ComponentObjectProp | ComponentUnknownProp)[]} 组件道具数组
*/
function getComponentPropsFromOptions(componentObject: ObjectExpression): (ComponentArrayProp | ComponentObjectProp | ComponentUnknownProp)[]
// example
createComponent({
properties: {
a: String // getComponentPropsFromOptions(node) => [propertiesNode...]
}
})
# getComputedProperties
通过查看所有组件的属性来获取所有计算属性
/**
* @param {ObjectExpression} componentObject 具有组件定义的对象
* @return {ComponentComputedProperty[]} 计算属性数组,格式为:[{key: String, value: ASTNode}]
*/
function getComputedProperties(componentObject:ObjectExpression): ComponentComputedProperty[]
// example
createComponent({
computed: {
foo() {} // getComponentPropsFromOptions(node) => [fooNode]
}
})
# isMpxFile
检测文件是不是.mpx后缀的文件
/**
* @param {string} path 文件路径
* @return {boolean} 如果是mpx文件则为true否则为fasle
*/
function isMpxFile(path:string): boolean
// example
isMpxFile('/src/index.mpx') => true
isMpxFile('/src/index.vue') => false
# compositingVisitors
合并Visitors
/**
* @template T
* @param {T} visitor
* @param {...(TemplateListener | RuleListener | NodeListener)} visitors
* @returns {T}
*/
function compositingVisitors(visitor:T,...visitors:): T
// example
compositingVisitors({
'VElement'(node) { /* doSomething */ }
},{
'VElement'(node) { /* doSomething */ }
}) => true
# executeOnMpx
检测当前文件是否是mpx文件且是创建mpx方法
/**
* @param {RuleContext} context ESLint 规则上下文对象。
* @param { (node: ObjectExpression, type: MpxObjectType) => void } cb 回调函数
*/
function executeOnMpx(context: RuleContext, cb: (node: ObjectExpression, type: MpxObjectType) => void)
// example
/*
* createComponent({
* data: {},
* methods: {}
* })
*/
executeOnMpx(context, (node, type) => {
const watchNode = utils.findProperty(node, 'methods')
if (!watchNode) return
})
# excuteOnMpxCreateApp
检测是否是在createApp函数并且执行callback
/**
* @param {RuleContext} context ESLint 规则上下文对象。
* @param { (node: ObjectExpression, type: MpxObjectType) => void } cb 回调函数
*/
function excuteOnMpxCreateApp(context: RuleContext, cb: (node: ObjectExpression, type: MpxObjectType) => void)
// example
/*
* createApp({
* onLaunch() {}
* })
*/
excuteOnMpxCreateApp(context, (node, type) => {
const onLanuchNode = utils.findProperty(node, 'onLaunch')
if (!onLanuchNode) return
})
# excuteOnMpxCreateComponent
检测是否是在createComponent函数并且执行callback
/**
* @param {RuleContext} context ESLint 规则上下文对象。
* @param { (node: ObjectExpression, type: MpxObjectType) => void } cb 回调函数
*/
function excuteOnMpxCreateComponent(context: RuleContext, cb: (node: ObjectExpression, type: MpxObjectType) => void)
// example
/*
* createComponent({
* properties:{}
* })
*/
excuteOnMpxCreateComponent(context, (node, type) => {
const propertiesNode = utils.findProperty(node, 'properties')
if (!propertiesNode) return
})
# excuteOnMpxCreatePage
检测是否是在createPage函数并且执行callback
/**
* @param {RuleContext} context ESLint 规则上下文对象。
* @param { (node: ObjectExpression, type: MpxObjectType) => void } cb 回调函数
*/
function excuteOnMpxCreatePage(context: RuleContext, cb: (node: ObjectExpression, type: MpxObjectType) => void)
// example
/*
* createPage({
* onPageScroll:{}
* })
*/
excuteOnMpxCreatePage(context, (node, type) => {
const onPageScrollNode = utils.findProperty(node, 'onPageScroll')
if (!onPageScrollNode) return
})
# getMpxObjectType
如果是在创建mpx实例,则返回内部对象
/**
* @param {ObjectExpression} node 检测的节点
* @returns { MpxObjectType | null } Mpx定义的节点
*/
function getMpxObjectType(node: ObjectExpression): MpxObjectType | null
// example
{
/** @param {ObjectExpression} node */
'ObjectExpression:exit'(node) {
const type = getMpxObjectType(node)
if (!type || type !== 'createComponent') return
}
}
# defineMpxVisitor
定义处理程序以遍历 Mpx 对象。
/**
* 定义处理程序以遍历 Mpx 对象。
* Visitor可以定义一些特别的事件
*
* - `onMpxObjectEnter` ... 找到 Mpx 对象时的事件。
* - `onMpxObjectExit` ... Mpx 对象访问结束时的事件。
* - `onSetupFunctionEnter` ... 找到设置功能时的事件。
* - `onRenderFunctionEnter` ... 找到渲染函数时的事件。
*
* @param {RuleContext} context ESLint 规则上下文对象。
* @param {MpxVisitor} visitor 访问者遍历 Mpx 对象。
*/
function defineMpxVisitor(context:RuleContext, visitor: MpxVisitor)
// example
defineMpxVisitor(context, {
onMpxObjectEnter(node) {
// 把所有computed里的属性收集起来
for (const computedProperty of utils.getComputedProperties(obj)) {
computedProperties.add(computedProperty)
}
}
})
# *iterateProperties
具有所有属性的返回generator
/**
* @param {ObjectExpression} node 检测的节点
* @param {Set<GroupName>} groups 需要检测的属性
* @returns {IterableIterator<ComponentPropertyData>}
*/
function *iterateProperties(node:ObjectExpression, groups: Set<GroupName>):IterableIterator<ComponentPropertyData>
// example
executeOnMpx(context, (obj) => {
const properties = iterateProperties(
obj,
new Set(['properties', 'computed'])
)
for (const o of properties) {
console.log(o)
}
})
# executeOnFunctionsWithoutReturn
查找所有没有返回值的函数
/**
* @param {boolean} treatUndefinedAsUnspecified
* @param { (node: ESNode) => void } cb 回调函数
* @returns {RuleListener}
*/
function executeOnFunctionsWithoutReturn(treatUndefinedAsUnspecified:boolean, cb:(node: ESNode) => void):RuleListener
example
参见return-in-computed-property (opens new window)
# isSingleLine
检测是否在同一行
/**
* @param {ASTNode} node
* @returns {boolean}
*/
function isSingleLine(node: ASTNode): boolean
// example
isSingleLine(node)
// <view>a</view> <view>b</view> true
// <view>a</view>
// <view>b</view> false
# hasInvalidEOF
检查程序的templateBody是否有无效的EOF。
/**
* @param {ASTNode} node
* @returns {boolean}
*/
function hasInvalidEOF(node: ASTNode): boolean
// example
// <template><view class="" true
// <template><view class=""></template> false
hasInvalidEOF(node)
# getMemberChaining
获取 MemberExpression 的链接节点。
/**
* @param {ESNode} node 解析的节点
* @return {[ESNode, ...MemberExpression[]]} 链接节点
*/
function getMemberChaining(node: ESNode): [ESNode, ...MemberExpression[]]
// example
getMemberChaining(node)
// const test = this.lorem['ipsum'].foo.bar` => [this, lorem, ipsum, foo, bar] 都为相应的节点
# editDistance
返回两个字符串editdistance
/**
* @param {string} a string a to compare
* @param {string} b string b to compare
* @returns {number}
*/
function editDistance(a:string, b:string):number
// example
// 对比两字符串差多少个字符
editDistance('book', 'back') => 2
editDistance('methods', 'metho') => 2
editDistance('computed', 'comput') => 2
# isProperty
检测节点是否为property节点
/**
* @param {Property | SpreadElement} node
* @returns {node is Property}
*/
function isProperty(node: Property | SpreadElement):boolean
// example
isProperty(node)
let a = {
a: 1 // true
}
function a () { // false
}
# isVElement
检测节点是否为VElement节点
/**
* @param {VElement | VExpressionContainer | VText} node
* @returns {node is VElement}
*/
function isVElement(node: VElement | VExpressionContainer | VText):boolean
// example
// <view class="a"></view> 如果node是view => true 如果node是class => false
isVElement(node)
# findProperty
从给定的 ObjectExpression 节点中查找具有给定名称的属性。
/**
* @param {ObjectExpression} node
* @param {string} name
* @param { (p: Property) => boolean } [filter]
* @returns { (Property) | null}
*/
function findProperty(node: ObjectExpression, name: string, filter?: (p: Property) => boolean):(Property) | null
// example
// 查找节点中的watch 过滤name=methods的节点
findProperty(node, 'watch', (n) => n.name === 'methods')
# isThis
检查给定节点是
this
还是存储this
的变量。
/**
* @param {ESNode} node 检测的节点
* @param {RuleContext} context 规则上下文
* @returns {boolean} 如果给的节点是this节点为true
*/
function isThis(node: ESNode, context: RuleContext): boolean
// example
function a () {
this.x = 1 // true
var that = this // true
var a = 1 // false
}
isThis(node, context)
# findMutating
检查给定节点是否有改变值的表达式
/**
* @param {MemberExpression|Identifier} props
* @returns { { kind: 'assignment' | 'update' | 'call' , node: ESNode, pathNodes: MemberExpression[] } | null }
*/
function findMutating(props: MemberExpression|Identifier): { kind: 'assignment' | 'update' | 'call' , node: ESNode, pathNodes: MemberExpression[] } | null
// example
findMutating(node)
// this.x += 1
// this.x++
// this.x.push(1)
// 这种值发生改变的会被选中
# equalTokens
检查两个给定节点的令牌是否相同。
/**
* @param {ASTNode} left 第一个节点
* @param {ASTNode} right 第二个节点
* @param {ParserServices.TokenStore | SourceCode} sourceCode 源码
* @returns {boolean} 是否相等
*/
function equalTokens(left: ASTNode, right: ASTNode, sourceCode: ParserServices.TokenStore | SourceCode): boolean
// <view wx:if="{{a}}">
// <view wx:elif="{{b}}">
// <view wx:elif="{{b}}">
// equalTokens(anode, bnode, '<view wx:...') false
// equalTokens(bnode, bnode, '<view wx:...') true
equalTokens(left, right, sourceCode)
# ✅ 使用 TypeScript 进行 JSDoc 类型检查
我们通过 TypeScript 和 JSDoc 启用了类型检查。
执行类型检查的命令是:npm run tsc
这只是为了帮助您编写规则,而不是进行严格的类型检查。 如果您发现难以解决类型检查警告,请随意使用 // @ts-nocheck
和 // @ts-ignore
注释来抑制警告。