Skip to content

Commit

Permalink
Merge pull request #24 from cwyyue/feat-properties-defualt
Browse files Browse the repository at this point in the history
feat: 校验properties
  • Loading branch information
pagnkelly authored Oct 19, 2023
2 parents a827c66 + b414a94 commit 35a0515
Show file tree
Hide file tree
Showing 8 changed files with 405 additions and 3 deletions.
1 change: 1 addition & 0 deletions docs/rules/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,4 @@ sidebarDepth: 0
| [mpx/valid-wx-key](./valid-wx-key.md) | 强制执行有效的 `wx:key` 指令 | |
| [mpx/valid-setup-define-expose](./valid-setup-define-expose.md) | setup-script模式下,template中使用的变量必须导出 | |
| [mpx/script-setup-uses-vars](./script-setup-uses-vars.md) | 防止`<script setup>``<template>`中使用的变量标记为未使用(已废弃,因为强制在`<script setup>`使用defineExpose导出变量) | |
| [mpx/valid-properties](./valid-properties.md) | 校验`properties`有效值 | |
95 changes: 95 additions & 0 deletions docs/rules/valid-properties.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
---
pageClass: rule-details
sidebarDepth: 0
title: mpx/valid-properties
description: 校验properties有效值
---
# mpx/valid-properties
> 校验properties有效值
- :gear: 这条规则包含在`"plugin:mpx/mpx-essential"`

此规则检查每个properties的值是否有效, 推荐开启ts获取更完善的类型校验。

## :book: 规则详情

保障properties的配置项有效,要求properties的配置项是只能是类型或对象。支持对`<script setup>`中的 defineProps 校验。

当配置项为对象时,只允许存在配置的键`(默认"type"、"value"、"optionalTypes"、"observer")`,强制要求配置`"type"`。不允许配置空对象,因为在微信低版本基础库会报错导致白屏;不允许其他无效键存在,例如"default"会在skyline下引起报错。

<eslint-code-block :rules="{'mpx/valid-properties': ['error']}">

```vue
<script>
// ✓ GOOD
createComponent({
properties: {
propsA: {
type: String,
value: ""
},
propsB: Object,
propsC: {
type: String,
optionalTypes:[Number]
}
}
})
// ✗ BAD
createComponent({
properties: {
propsA: {},
propsB: '',
propsC: {
type: Object,
default: {}
},
propsD: [1,2],
propsE: {
value: {}
}
}
})
</script>
```
</eslint-code-block>

<eslint-code-block :rules="{'mpx/valid-properties': ['error']}">
```vue
<script setup>
// ✓ GOOD
const props = defineProps({
propsA: {
type: Object,
value: {}
},
propsB: Array
})
// ✗ BAD
const badProps = defineProps({
propsA: {
type: Object,
default: {}
},
propsB: {}
})
</script>
```
</eslint-code-block>

## :wrench: 选项
可配置allowKeys选项,允许properties的配置对象存在这些key。默认为`"type", "value", "optionalTypes", "observer"`
```json
{
"mpx/valid-wx-key": ["error", {
"allowKeys": ["type", "value","optionalTypes","observer"]
}]
}
```

## :mag: 具体实现

- [规则](https://github.com/mpx-ecology/eslint-plugin-mpx/blob/master/lib/rules/valid-properties.js)
- [测试](https://github.com/mpx-ecology/eslint-plugin-mpx/blob/master/tests/lib/rules/valid-properties.js)
3 changes: 2 additions & 1 deletion lib/configs/mpx-essential.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ module.exports = {
'mpx/valid-wx-key': 'error',
'mpx/valid-attribute-value': 'error',
'mpx/valid-template-quote': 'error',
'mpx/valid-component-range': 'error'
'mpx/valid-component-range': 'error',
'mpx/valid-properties': 'error'
}
}
3 changes: 2 additions & 1 deletion lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ module.exports = {
'valid-initdata': require('./rules/valid-initdata'),
'no-deprecated-lifecycle': require('./rules/no-deprecated-lifecycle'),
'no-deprecated-mpx-createfunction': require('./rules/no-deprecated-mpx-createfunction'),
'no-deprecated-watch-second-param': require('./rules/no-deprecated-watch-second-param')
'no-deprecated-watch-second-param': require('./rules/no-deprecated-watch-second-param'),
'valid-properties': require('./rules/valid-properties')
},
configs: {
base: require('./configs/base'),
Expand Down
151 changes: 151 additions & 0 deletions lib/rules/valid-properties.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/**
* @author pagnkelly
* @copyright 2020 pagnkelly. All rights reserved.
* See LICENSE file in root directory for full license.
*/
'use strict'

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

const utils = require('../utils')

/**
* @typedef {import("../utils").ComponentObjectProp} ComponentObjectProp
*/

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------
// 校验properties是否符合预期
/** 默认允许的key */
const DEFAULT_KEYS = ['type', 'value', 'optionalTypes', 'observer']
/**
* check prop
* @param {Property} node
* @returns
*/
/**
*
* @param {Property} node
* @param {RuleContext} context
* @param {string[]} allowKeys
* @returns
*/
function validProp(node, context, allowKeys) {
if (!node) return
const sourceCode = context.getSourceCode()
const propName = sourceCode.getText(node.key)
if (!node.value) {
return context.report({
node,
message: "The value of '{{propName}}' cannot be empty.",
data: {
propName
}
})
}
if (node.value.type === 'ObjectExpression') {
if (!node.value.properties.length) {
return context.report({
node: node.value,
message: "The value of '{{propName}}' cannot be empty object.",
data: {
propName
}
})
}
let hasType = 0
node.value.properties.forEach((item) => {
if (item.type !== 'Property') return
const keyName = sourceCode.getText(item.key)
if (!allowKeys.includes(keyName)) {
context.report({
node: item,
message: "Property '{{propName}}' has invalid key '{{keyName}}'.",
data: {
propName,
keyName
}
})
} else if (keyName === 'type') {
hasType = 1
}
})
if (!hasType) {
context.report({
node: node.value,
message: "Property '{{propName}}' requires 'type' key.",
data: {
propName
}
})
}
} else if (node.value.type !== 'Identifier') {
return context.report({
node,
message: "Invalid value for '{{propName}}'.",
data: {
propName
}
})
}
}
module.exports = {
meta: {
type: 'problem',
docs: {
description:
'enforce that a return statement is present in computed property',
categories: ['mpx-essential'],
url: 'https://mpx-ecology.github.io/eslint-plugin-mpx/rules/valid-properties.html'
},
fixable: null, // or "code" or "whitespace"
schema: [
{
type: 'object',
properties: {
allowKeys: {
type: 'array'
}
}
}
]
},
/** @param {RuleContext} context */
create(context) {
const options = context.options[0] || {}
const allowKeys = options.allowKeys || DEFAULT_KEYS
const isScriptSetup = utils.isScriptSetup(context)

// ----------------------------------------------------------------------
// Public
// ----------------------------------------------------------------------

if (isScriptSetup) {
return utils.defineTemplateBodyVisitor(
context,
{},
{
/**
* @param {ObjectExpression} node
*/
"CallExpression[callee.name='defineProps'] > ObjectExpression"(node) {
for (const prop of utils.getComponentPropsFromDefine(node)) {
prop.type === 'object' && validProp(prop.node, context, allowKeys)
}
}
}
)
} else {
return utils.defineMpxVisitor(context, {
onMpxObjectEnter(obj) {
for (const prop of utils.getComponentPropsFromOptions(obj)) {
prop.type === 'object' && validProp(prop.node, context, allowKeys)
}
}
})
}
}
}
2 changes: 2 additions & 0 deletions lib/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -803,6 +803,8 @@ module.exports = {
* @return {(ComponentArrayProp | ComponentObjectProp | ComponentUnknownProp)[]} Array of component props
*/
getComponentPropsFromOptions,

getComponentPropsFromDefine,
/**
* Get all computed properties by looking at all component's properties
* @param {ObjectExpression} componentObject Object with component definition
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"scripts": {
"start": "npm run test:base -- --watch --growl",
"test": "mocha \"tests/lib/**/*.js\" --reporter dot",
"test:only": "mocha \"tests/lib/rules/valid-initdata.js\" --reporter dot",
"test:only": "mocha \"tests/lib/rules/valid-properties.js\" --reporter dot",
"debug": "mocha --inspect \"tests/lib/**/*.js\" --reporter dot --timeout 60000",
"cover": "npm run cover:test && npm run cover:report",
"cover:test": "nyc npm run test -- --timeout 60000",
Expand Down
Loading

0 comments on commit 35a0515

Please sign in to comment.