From 3f2f92564174ebf74e5a0a18e1f7d6183e7ccf47 Mon Sep 17 00:00:00 2001 From: "chenrongyan.cry" Date: Mon, 23 Aug 2021 15:04:15 +0800 Subject: [PATCH 1/4] wip --- .../.eslintrc.js | 19 + .../README.md | 46 ++ .../docs/rules/no-inline-styles.md | 35 ++ .../docs/rules/no-spread-props.md | 35 ++ .../package.json | 32 ++ .../src/index.js | 19 + .../src/rules/no-inline-styles.js | 56 ++ .../src/rules/no-spread-props.js | 48 ++ .../src/utils/docUrl.js | 7 + .../src/utils/props.js | 2 + .../src/utils/stylesheet.js | 491 ++++++++++++++++++ .../tests/src/rules/no-inline-style.js | 58 +++ .../tests/src/rules/no-spread-props.js | 34 ++ 13 files changed, 882 insertions(+) create mode 100644 packages/eslint-plugin-rax-runtime-miniapp/.eslintrc.js create mode 100644 packages/eslint-plugin-rax-runtime-miniapp/README.md create mode 100644 packages/eslint-plugin-rax-runtime-miniapp/docs/rules/no-inline-styles.md create mode 100644 packages/eslint-plugin-rax-runtime-miniapp/docs/rules/no-spread-props.md create mode 100644 packages/eslint-plugin-rax-runtime-miniapp/package.json create mode 100644 packages/eslint-plugin-rax-runtime-miniapp/src/index.js create mode 100644 packages/eslint-plugin-rax-runtime-miniapp/src/rules/no-inline-styles.js create mode 100644 packages/eslint-plugin-rax-runtime-miniapp/src/rules/no-spread-props.js create mode 100644 packages/eslint-plugin-rax-runtime-miniapp/src/utils/docUrl.js create mode 100644 packages/eslint-plugin-rax-runtime-miniapp/src/utils/props.js create mode 100644 packages/eslint-plugin-rax-runtime-miniapp/src/utils/stylesheet.js create mode 100644 packages/eslint-plugin-rax-runtime-miniapp/tests/src/rules/no-inline-style.js create mode 100644 packages/eslint-plugin-rax-runtime-miniapp/tests/src/rules/no-spread-props.js diff --git a/packages/eslint-plugin-rax-runtime-miniapp/.eslintrc.js b/packages/eslint-plugin-rax-runtime-miniapp/.eslintrc.js new file mode 100644 index 00000000..2f33f7a7 --- /dev/null +++ b/packages/eslint-plugin-rax-runtime-miniapp/.eslintrc.js @@ -0,0 +1,19 @@ +"use strict"; + +module.exports = { + root: true, + extends: [ + "eslint:recommended", + "plugin:eslint-plugin/recommended", + "plugin:node/recommended", + ], + env: { + node: true, + }, + overrides: [ + { + files: ["tests/**/*.js"], + env: { mocha: true }, + }, + ], +}; diff --git a/packages/eslint-plugin-rax-runtime-miniapp/README.md b/packages/eslint-plugin-rax-runtime-miniapp/README.md new file mode 100644 index 00000000..98892a96 --- /dev/null +++ b/packages/eslint-plugin-rax-runtime-miniapp/README.md @@ -0,0 +1,46 @@ +# eslint-plugin-rax-runtime-miniapp + +rax 运行时 eslint 插件 + +## Installation + +You'll first need to install [ESLint](https://eslint.org/): + +```sh +npm i eslint --save-dev +``` + +Next, install `eslint-plugin-rax-runtime-miniapp`: + +```sh +npm install eslint-plugin-rax-runtime-miniapp --save-dev +``` + +## Usage + +Add `rax-runtime-miniapp` to the plugins section of your `.eslintrc` configuration file. You can omit the `eslint-plugin-` prefix: + +```json +{ + "plugins": [ + "rax-runtime-miniapp" + ] +} +``` + + +Then configure the rules you want to use under the rules section. + +```json +{ + "rules": { + "rax-runtime-miniapp/rule-name": 2 + } +} +``` + +## Supported Rules + +* Fill in provided rules here + + diff --git a/packages/eslint-plugin-rax-runtime-miniapp/docs/rules/no-inline-styles.md b/packages/eslint-plugin-rax-runtime-miniapp/docs/rules/no-inline-styles.md new file mode 100644 index 00000000..ff4effa4 --- /dev/null +++ b/packages/eslint-plugin-rax-runtime-miniapp/docs/rules/no-inline-styles.md @@ -0,0 +1,35 @@ +# 不推荐使用内联样式 (no-inline-style) + +Please describe the origin of the rule here. + +## Rule Details + +This rule aims to... + +Examples of **incorrect** code for this rule: + +```js + +// fill me in + +``` + +Examples of **correct** code for this rule: + +```js + +// fill me in + +``` + +### Options + +If there are any options, describe them here. Otherwise, delete this section. + +## When Not To Use It + +Give a short description of when it would be appropriate to turn off this rule. + +## Further Reading + +If there are other links that describe the issue this rule addresses, please include them here in a bulleted list. diff --git a/packages/eslint-plugin-rax-runtime-miniapp/docs/rules/no-spread-props.md b/packages/eslint-plugin-rax-runtime-miniapp/docs/rules/no-spread-props.md new file mode 100644 index 00000000..b78e167b --- /dev/null +++ b/packages/eslint-plugin-rax-runtime-miniapp/docs/rules/no-spread-props.md @@ -0,0 +1,35 @@ +# 不推荐使用 {..props} 的方式传参 (no-spread-props) + +Please describe the origin of the rule here. + +## Rule Details + +This rule aims to... + +Examples of **incorrect** code for this rule: + +```js + +// fill me in + +``` + +Examples of **correct** code for this rule: + +```js + +// fill me in + +``` + +### Options + +If there are any options, describe them here. Otherwise, delete this section. + +## When Not To Use It + +Give a short description of when it would be appropriate to turn off this rule. + +## Further Reading + +If there are other links that describe the issue this rule addresses, please include them here in a bulleted list. diff --git a/packages/eslint-plugin-rax-runtime-miniapp/package.json b/packages/eslint-plugin-rax-runtime-miniapp/package.json new file mode 100644 index 00000000..2bfa04d4 --- /dev/null +++ b/packages/eslint-plugin-rax-runtime-miniapp/package.json @@ -0,0 +1,32 @@ +{ + "name": "eslint-plugin-rax-runtime-miniapp", + "version": "0.0.1-0", + "description": "rax 运行时 eslint 插件", + "keywords": [ + "eslint", + "eslintplugin", + "eslint-plugin" + ], + "author": "chenrongyan", + "main": "lib/index.js", + "scripts": { + "lint": "eslint .", + "test": "mocha tests --recursive" + }, + "dependencies": { + "requireindex": "^1.1.0" + }, + "devDependencies": { + "eslint": "^7.1.0", + "eslint-plugin-eslint-plugin": "^3.2.0", + "eslint-plugin-node": "^11.0.0", + "mocha": "^9.0.0" + }, + "engines": { + "node": "12.x || 14.x || >= 16" + }, + "peerDependencies": { + "eslint": ">=6" + }, + "license": "ISC" +} diff --git a/packages/eslint-plugin-rax-runtime-miniapp/src/index.js b/packages/eslint-plugin-rax-runtime-miniapp/src/index.js new file mode 100644 index 00000000..4ccdf608 --- /dev/null +++ b/packages/eslint-plugin-rax-runtime-miniapp/src/index.js @@ -0,0 +1,19 @@ +/** + * @fileoverview rax 运行时 eslint 插件 + * @author chenrongyan + */ +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const requireIndex = require("requireindex"); + +//------------------------------------------------------------------------------ +// Plugin Definition +//------------------------------------------------------------------------------ + + +// import all rules in lib/rules +module.exports.rules = requireIndex(__dirname + "/rules"); diff --git a/packages/eslint-plugin-rax-runtime-miniapp/src/rules/no-inline-styles.js b/packages/eslint-plugin-rax-runtime-miniapp/src/rules/no-inline-styles.js new file mode 100644 index 00000000..0827995b --- /dev/null +++ b/packages/eslint-plugin-rax-runtime-miniapp/src/rules/no-inline-styles.js @@ -0,0 +1,56 @@ +/** + * @fileoverview 不推荐使用内联样式 + * @author chenrongyan + */ +"use strict"; + +const styleSheet = require('../utils/stylesheet'); +const docUrl = require('../utils/docUrl'); + +const util = require('util'); + +const { StyleSheets } = styleSheet; +const { astHelpers } = styleSheet; + +module.exports = { + meta: { + type: 'suggestion', + docs: { + description: "Using inline styles in runtime miniapp is not recommended", + recommended: false, + url: docUrl('no-inline-styles') + }, + fixable: null, + schema: [], + }, + + create(context) { + const styleSheets = new StyleSheets(); + + function reportInlineStyles(inlineStyles) { + if (inlineStyles) { + inlineStyles.forEach((style) => { + if (style) { + const expression = util.inspect(style.expression); + context.report({ + node: style.node, + message: 'Inline style: {{ expression }}', + data: { expression }, + }); + } + }); + } + } + + return { + JSXAttribute: (node) => { + if (astHelpers.isStyleAttribute(node)) { + const styles = astHelpers.collectStyleObjectExpressions(node.value, context); + styleSheets.addObjectExpressions(styles); + } + }, + + 'Program:exit': () => reportInlineStyles(styleSheets.getObjectExpressions()), + } + } +}; diff --git a/packages/eslint-plugin-rax-runtime-miniapp/src/rules/no-spread-props.js b/packages/eslint-plugin-rax-runtime-miniapp/src/rules/no-spread-props.js new file mode 100644 index 00000000..848cb2da --- /dev/null +++ b/packages/eslint-plugin-rax-runtime-miniapp/src/rules/no-spread-props.js @@ -0,0 +1,48 @@ +/** + * @fileoverview 不推荐使用 {..props} 的方式传参 + * @author chenrongyan + */ +"use strict"; + +const util = require('util'); +const docUrl = require('../utils/docUrl'); + +module.exports = { + meta: { + type: 'suggestion', + docs: { + description: "{...props} in runtime miniapp is not recommended", + recommended: false, + url: docUrl('no-spread-props'), + }, + fixable: null, // Or `code` or `whitespace` + schema: [], // Add a schema if the rule has options + }, + + create(context) { + const objectExpressions = []; + function reportSpreadProps(spreadProps) { + if (spreadProps) { + spreadProps.forEach(prop => { + console.log(prop); + if (prop) { + const expression = util.inspect(prop.expression); + context.report({ + node: prop.node, + message: 'Spread style: {{ expression }}', + data: { expression } + }) + } + }) + } + } + + return { + JSXSpreadAttribute: (node) => { + objectExpressions.push(node); + }, + + 'Program:exit': () => reportSpreadProps(objectExpressions) + }; + }, +}; diff --git a/packages/eslint-plugin-rax-runtime-miniapp/src/utils/docUrl.js b/packages/eslint-plugin-rax-runtime-miniapp/src/utils/docUrl.js new file mode 100644 index 00000000..ef638a9f --- /dev/null +++ b/packages/eslint-plugin-rax-runtime-miniapp/src/utils/docUrl.js @@ -0,0 +1,7 @@ +/** + * 返回用户可查看的 doc url + */ +module.exports = function docUrl(docName) { + const repoUrl = 'https://github.com/raxjs/miniapp/tree/master/packages/eslint-plugin-rax-runtime-miniapp'; + return `${repoUrl}/docs/rules/${docName}.md`; +} \ No newline at end of file diff --git a/packages/eslint-plugin-rax-runtime-miniapp/src/utils/props.js b/packages/eslint-plugin-rax-runtime-miniapp/src/utils/props.js new file mode 100644 index 00000000..eb109abb --- /dev/null +++ b/packages/eslint-plugin-rax-runtime-miniapp/src/utils/props.js @@ -0,0 +1,2 @@ +'use strict'; + diff --git a/packages/eslint-plugin-rax-runtime-miniapp/src/utils/stylesheet.js b/packages/eslint-plugin-rax-runtime-miniapp/src/utils/stylesheet.js new file mode 100644 index 00000000..40ea13c4 --- /dev/null +++ b/packages/eslint-plugin-rax-runtime-miniapp/src/utils/stylesheet.js @@ -0,0 +1,491 @@ +'use strict'; + +/** + * StyleSheets represents the StyleSheets found in the source code. + * @constructor + */ +function StyleSheets() { + this.styleSheets = {}; +} + +/** + * Add adds a StyleSheet to our StyleSheets collections. + * + * @param {string} styleSheetName - The name of the StyleSheet. + * @param {object} properties - The collection of rules in the styleSheet. + */ +StyleSheets.prototype.add = function (styleSheetName, properties) { + this.styleSheets[styleSheetName] = properties; +}; + +/** + * MarkAsUsed marks a rule as used in our source code by removing it from the + * specified StyleSheet rules. + * + * @param {string} fullyQualifiedName - The fully qualified name of the rule. + * for example 'styles.text' + */ +StyleSheets.prototype.markAsUsed = function (fullyQualifiedName) { + const nameSplit = fullyQualifiedName.split('.'); + const styleSheetName = nameSplit[0]; + const styleSheetProperty = nameSplit[1]; + + if (this.styleSheets[styleSheetName]) { + this.styleSheets[styleSheetName] = this + .styleSheets[styleSheetName] + .filter((property) => property.key.name !== styleSheetProperty); + } +}; + +/** + * GetUnusedReferences returns all collected StyleSheets and their + * unmarked rules. + */ +StyleSheets.prototype.getUnusedReferences = function () { + return this.styleSheets; +}; + +/** + * AddColorLiterals adds an array of expressions that contain color literals + * to the ColorLiterals collection + * @param {array} expressions - an array of expressions containing color literals + */ +StyleSheets.prototype.addColorLiterals = function (expressions) { + if (!this.colorLiterals) { + this.colorLiterals = []; + } + this.colorLiterals = this.colorLiterals.concat(expressions); +}; + +/** + * GetColorLiterals returns an array of collected color literals expressions + * @returns {Array} + */ +StyleSheets.prototype.getColorLiterals = function () { + return this.colorLiterals; +}; + +/** + * AddObjectexpressions adds an array of expressions to the ObjectExpressions collection + * @param {Array} expressions - an array of expressions containing ObjectExpressions in + * inline styles + */ +StyleSheets.prototype.addObjectExpressions = function (expressions) { + if (!this.objectExpressions) { + this.objectExpressions = []; + } + this.objectExpressions = this.objectExpressions.concat(expressions); +}; + +/** + * GetObjectExpressions returns an array of collected object expressiosn used in inline styles + * @returns {Array} + */ +StyleSheets.prototype.getObjectExpressions = function () { + return this.objectExpressions; +}; + + +let currentContent; +const getSourceCode = (node) => currentContent + .getSourceCode(node) + .getText(node); + +const getStyleSheetObjectNames = (settings) => settings['react-native/style-sheet-object-names'] || ['StyleSheet']; + +const astHelpers = { + containsStyleSheetObject: function (node, objectNames) { + return Boolean( + node + && node.type === 'CallExpression' + && node.callee + && node.callee.object + && node.callee.object.name + && objectNames.includes(node.callee.object.name) + ); + }, + + containsCreateCall: function (node) { + return Boolean( + node + && node.callee + && node.callee.property + && node.callee.property.name === 'create' + ); + }, + + isStyleSheetDeclaration: function (node, settings) { + const objectNames = getStyleSheetObjectNames(settings); + + return Boolean( + astHelpers.containsStyleSheetObject(node, objectNames) + && astHelpers.containsCreateCall(node) + ); + }, + + getStyleSheetName: function (node) { + if (node && node.parent && node.parent.id) { + return node.parent.id.name; + } + }, + + getStyleDeclarations: function (node) { + if ( + node + && node.type === 'CallExpression' + && node.arguments + && node.arguments[0] + && node.arguments[0].properties + ) { + return node.arguments[0].properties.filter((property) => property.type === 'Property'); + } + + return []; + }, + + getStyleDeclarationsChunks: function (node) { + if ( + node + && node.type === 'CallExpression' + && node.arguments + && node.arguments[0] + && node.arguments[0].properties + ) { + const { properties } = node.arguments[0]; + + const result = []; + let chunk = []; + for (let i = 0; i < properties.length; i += 1) { + const property = properties[i]; + if (property.type === 'Property') { + chunk.push(property); + } else if (chunk.length) { + result.push(chunk); + chunk = []; + } + } + if (chunk.length) { + result.push(chunk); + } + return result; + } + + return []; + }, + + getPropertiesChunks: function (properties) { + const result = []; + let chunk = []; + for (let i = 0; i < properties.length; i += 1) { + const property = properties[i]; + if (property.type === 'Property') { + chunk.push(property); + } else if (chunk.length) { + result.push(chunk); + chunk = []; + } + } + if (chunk.length) { + result.push(chunk); + } + return result; + }, + + getExpressionIdentifier: function (node) { + if (node) { + switch (node.type) { + case 'Identifier': + return node.name; + case 'Literal': + return node.value; + case 'TemplateLiteral': + return node.quasis.reduce((result, quasi, index) => result + + quasi.value.cooked + + astHelpers.getExpressionIdentifier(node.expressions[index]), + ''); + default: + return ''; + } + } + + return ''; + }, + + getStylePropertyIdentifier: function (node) { + if ( + node + && node.key + ) { + return astHelpers.getExpressionIdentifier(node.key); + } + }, + + isStyleAttribute: function (node) { + return Boolean( + node.type === 'JSXAttribute' + && node.name + && node.name.name + && node.name.name.toLowerCase().includes('style') + ); + }, + + collectStyleObjectExpressions: function (node, context) { + currentContent = context; + if (astHelpers.hasArrayOfStyleReferences(node)) { + const styleReferenceContainers = node + .expression + .elements; + + return astHelpers.collectStyleObjectExpressionFromContainers( + styleReferenceContainers + ); + } if (node && node.expression) { + return astHelpers.getStyleObjectExpressionFromNode(node.expression); + } + + return []; + }, + + collectColorLiterals: function (node, context) { + if (!node) { + return []; + } + + currentContent = context; + if (astHelpers.hasArrayOfStyleReferences(node)) { + const styleReferenceContainers = node + .expression + .elements; + + return astHelpers.collectColorLiteralsFromContainers( + styleReferenceContainers + ); + } + + if (node.type === 'ObjectExpression') { + return astHelpers.getColorLiteralsFromNode(node); + } + + return astHelpers.getColorLiteralsFromNode(node.expression); + }, + + collectStyleObjectExpressionFromContainers: function (nodes) { + let objectExpressions = []; + nodes.forEach((node) => { + objectExpressions = objectExpressions + .concat(astHelpers.getStyleObjectExpressionFromNode(node)); + }); + + return objectExpressions; + }, + + collectColorLiteralsFromContainers: function (nodes) { + let colorLiterals = []; + nodes.forEach((node) => { + colorLiterals = colorLiterals + .concat(astHelpers.getColorLiteralsFromNode(node)); + }); + + return colorLiterals; + }, + + getStyleReferenceFromNode: function (node) { + let styleReference; + let leftStyleReferences; + let rightStyleReferences; + + if (!node) { + return []; + } + + switch (node.type) { + case 'MemberExpression': + styleReference = astHelpers.getStyleReferenceFromExpression(node); + return [styleReference]; + case 'LogicalExpression': + leftStyleReferences = astHelpers.getStyleReferenceFromNode(node.left); + rightStyleReferences = astHelpers.getStyleReferenceFromNode(node.right); + return [].concat(leftStyleReferences).concat(rightStyleReferences); + case 'ConditionalExpression': + leftStyleReferences = astHelpers.getStyleReferenceFromNode(node.consequent); + rightStyleReferences = astHelpers.getStyleReferenceFromNode(node.alternate); + return [].concat(leftStyleReferences).concat(rightStyleReferences); + default: + return []; + } + }, + + getStyleObjectExpressionFromNode: function (node) { + let leftStyleObjectExpression; + let rightStyleObjectExpression; + + if (!node) { + return []; + } + + if (node.type === 'ObjectExpression') { + return [astHelpers.getStyleObjectFromExpression(node)]; + } + + switch (node.type) { + case 'LogicalExpression': + leftStyleObjectExpression = astHelpers.getStyleObjectExpressionFromNode(node.left); + rightStyleObjectExpression = astHelpers.getStyleObjectExpressionFromNode(node.right); + return [].concat(leftStyleObjectExpression).concat(rightStyleObjectExpression); + case 'ConditionalExpression': + leftStyleObjectExpression = astHelpers.getStyleObjectExpressionFromNode(node.consequent); + rightStyleObjectExpression = astHelpers.getStyleObjectExpressionFromNode(node.alternate); + return [].concat(leftStyleObjectExpression).concat(rightStyleObjectExpression); + default: + return []; + } + }, + + getColorLiteralsFromNode: function (node) { + let leftColorLiterals; + let rightColorLiterals; + + if (!node) { + return []; + } + + if (node.type === 'ObjectExpression') { + return [astHelpers.getColorLiteralsFromExpression(node)]; + } + + switch (node.type) { + case 'LogicalExpression': + leftColorLiterals = astHelpers.getColorLiteralsFromNode(node.left); + rightColorLiterals = astHelpers.getColorLiteralsFromNode(node.right); + return [].concat(leftColorLiterals).concat(rightColorLiterals); + case 'ConditionalExpression': + leftColorLiterals = astHelpers.getColorLiteralsFromNode(node.consequent); + rightColorLiterals = astHelpers.getColorLiteralsFromNode(node.alternate); + return [].concat(leftColorLiterals).concat(rightColorLiterals); + default: + return []; + } + }, + + hasArrayOfStyleReferences: function (node) { + return node && Boolean( + node.type === 'JSXExpressionContainer' + && node.expression + && node.expression.type === 'ArrayExpression' + ); + }, + + getStyleReferenceFromExpression: function (node) { + const result = []; + const name = astHelpers.getObjectName(node); + if (name) { + result.push(name); + } + + const property = astHelpers.getPropertyName(node); + if (property) { + result.push(property); + } + + return result.join('.'); + }, + + getStyleObjectFromExpression: function (node) { + const obj = {}; + let invalid = false; + if (node.properties && node.properties.length) { + node.properties.forEach((p) => { + if (!p.value || !p.key) { + return; + } + if (p.value.type === 'Literal') { + invalid = true; + obj[p.key.name] = p.value.value; + } else if (p.value.type === 'ConditionalExpression') { + const innerNode = p.value; + if (innerNode.consequent.type === 'Literal' || innerNode.alternate.type === 'Literal') { + invalid = true; + obj[p.key.name] = getSourceCode(innerNode); + } + } else if (p.value.type === 'UnaryExpression' && p.value.operator === '-' && p.value.argument.type === 'Literal') { + invalid = true; + obj[p.key.name] = -1 * p.value.argument.value; + } else if (p.value.type === 'UnaryExpression' && p.value.operator === '+' && p.value.argument.type === 'Literal') { + invalid = true; + obj[p.key.name] = p.value.argument.value; + } + }); + } + return invalid ? { expression: obj, node: node } : undefined; + }, + + getColorLiteralsFromExpression: function (node) { + const obj = {}; + let invalid = false; + if (node.properties && node.properties.length) { + node.properties.forEach((p) => { + if (p.key && p.key.name && p.key.name.toLowerCase().indexOf('color') !== -1) { + if (p.value.type === 'Literal') { + invalid = true; + obj[p.key.name] = p.value.value; + } else if (p.value.type === 'ConditionalExpression') { + const innerNode = p.value; + if (innerNode.consequent.type === 'Literal' || innerNode.alternate.type === 'Literal') { + invalid = true; + obj[p.key.name] = getSourceCode(innerNode); + } + } + } + }); + } + return invalid ? { expression: obj, node: node } : undefined; + }, + + getObjectName: function (node) { + if ( + node + && node.object + && node.object.name + ) { + return node.object.name; + } + }, + + getPropertyName: function (node) { + if ( + node + && node.property + && node.property.name + ) { + return node.property.name; + } + }, + + getPotentialStyleReferenceFromMemberExpression: function (node) { + if ( + node + && node.object + && node.object.type === 'Identifier' + && node.object.name + && node.property + && node.property.type === 'Identifier' + && node.property.name + && node.parent.type !== 'MemberExpression' + ) { + return [node.object.name, node.property.name].join('.'); + } + }, + + isEitherShortHand: function (property1, property2) { + const shorthands = ['margin', 'padding', 'border', 'flex']; + if (shorthands.includes(property1)) { + return property2.startsWith(property1); + } if (shorthands.includes(property2)) { + return property1.startsWith(property2); + } + return false; + }, +}; + +module.exports.astHelpers = astHelpers; +module.exports.StyleSheets = StyleSheets; diff --git a/packages/eslint-plugin-rax-runtime-miniapp/tests/src/rules/no-inline-style.js b/packages/eslint-plugin-rax-runtime-miniapp/tests/src/rules/no-inline-style.js new file mode 100644 index 00000000..2c96f75f --- /dev/null +++ b/packages/eslint-plugin-rax-runtime-miniapp/tests/src/rules/no-inline-style.js @@ -0,0 +1,58 @@ +/** + * @fileoverview 不推荐使用内联样式 + * @author chenrongyan + */ +"use strict"; + +const rule = require("../../../src/rules/no-inline-styles"), + RuleTester = require("eslint").RuleTester; + +const ruleTester = new RuleTester({ + parserOptions: { + ecmaVersion: 10, + ecmaFeatures: { + jsx: true, + }, + }, +}); + +ruleTester.run("no-inline-style", rule, { + valid: [ + `function Hello() { + return ( + Hello + ); + }`, + `function Hello(props) { + return ( + Hello + ); + }` + ], + + invalid: [ + { + code: ` + function Hello() { + return ( + Hello + ); + } + `, + errors: [{ + message: 'Inline style: { backgroundColor: \'#fff\' }' + }], + }, { + code: ` + function Hello(props) { + return ( + Hello + ); + } + `, + errors: [{ + message: 'Inline style: { backgroundColor: \'#fff\' }' + }] + } + ], +}); diff --git a/packages/eslint-plugin-rax-runtime-miniapp/tests/src/rules/no-spread-props.js b/packages/eslint-plugin-rax-runtime-miniapp/tests/src/rules/no-spread-props.js new file mode 100644 index 00000000..abbaad1d --- /dev/null +++ b/packages/eslint-plugin-rax-runtime-miniapp/tests/src/rules/no-spread-props.js @@ -0,0 +1,34 @@ +/** + * @fileoverview 不推荐使用 {..props} 的方式传参 + * @author chenrongyan + */ +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require("../../../src/rules/no-inline-styles"), + RuleTester = require("eslint").RuleTester; + +const ruleTester = new RuleTester({ + parserOptions: { + ecmaVersion: 10, + ecmaFeatures: { + jsx: true, + }, + }, +}); + +ruleTester.run("no-spread-props", rule, { + valid: [ + // give me some code that won't trigger a warning + ], + + invalid: [ + { + code: "", + errors: [{ message: "Fill me in.", type: "Me too" }], + }, + ], +}); From 50c9d21c593fb4d38e2319b80620dba38599967a Mon Sep 17 00:00:00 2001 From: "chenrongyan.cry" Date: Mon, 23 Aug 2021 15:37:25 +0800 Subject: [PATCH 2/4] feat: add eslint-plugin-rax-runtime-miniapp --- .../README.md | 4 +- .../docs/rules/no-inline-styles.md | 37 +- .../docs/rules/no-spread-props.md | 35 -- .../package.json | 4 +- .../src/rules/no-spread-props.js | 48 -- .../src/utils/index.js | 354 +++++++++++++++ .../src/utils/props.js | 2 - .../src/utils/stylesheet.js | 409 ++---------------- .../tests/src/rules/no-spread-props.js | 34 -- 9 files changed, 405 insertions(+), 522 deletions(-) delete mode 100644 packages/eslint-plugin-rax-runtime-miniapp/docs/rules/no-spread-props.md delete mode 100644 packages/eslint-plugin-rax-runtime-miniapp/src/rules/no-spread-props.js create mode 100644 packages/eslint-plugin-rax-runtime-miniapp/src/utils/index.js delete mode 100644 packages/eslint-plugin-rax-runtime-miniapp/src/utils/props.js delete mode 100644 packages/eslint-plugin-rax-runtime-miniapp/tests/src/rules/no-spread-props.js diff --git a/packages/eslint-plugin-rax-runtime-miniapp/README.md b/packages/eslint-plugin-rax-runtime-miniapp/README.md index 98892a96..de42df3b 100644 --- a/packages/eslint-plugin-rax-runtime-miniapp/README.md +++ b/packages/eslint-plugin-rax-runtime-miniapp/README.md @@ -41,6 +41,8 @@ Then configure the rules you want to use under the rules section. ## Supported Rules -* Fill in provided rules here +* no-inline-styles + +[no-inline-styles](https://github.com/raxjs/miniapp/tree/master/packages/eslint-plugin-rax-runtime-miniapp/docs/rules/no-inline-styles.md) diff --git a/packages/eslint-plugin-rax-runtime-miniapp/docs/rules/no-inline-styles.md b/packages/eslint-plugin-rax-runtime-miniapp/docs/rules/no-inline-styles.md index ff4effa4..57af1f82 100644 --- a/packages/eslint-plugin-rax-runtime-miniapp/docs/rules/no-inline-styles.md +++ b/packages/eslint-plugin-rax-runtime-miniapp/docs/rules/no-inline-styles.md @@ -1,35 +1,34 @@ # 不推荐使用内联样式 (no-inline-style) -Please describe the origin of the rule here. +在运行时小程序中,内联样式会导致 `setData` 传输的数据体积变大。本规则会检测元素的内联样式,当内联样式的每一项属性或属性值均为变量时,不会报错。 ## Rule Details -This rule aims to... Examples of **incorrect** code for this rule: ```js +function Hello() { + return ( + Hello + ); +} +``` -// fill me in - +```js +function Hello(props) { + return ( + Hello + ); +} ``` Examples of **correct** code for this rule: ```js - -// fill me in - +function Hello(props) { + return ( + Hello + ); +} ``` - -### Options - -If there are any options, describe them here. Otherwise, delete this section. - -## When Not To Use It - -Give a short description of when it would be appropriate to turn off this rule. - -## Further Reading - -If there are other links that describe the issue this rule addresses, please include them here in a bulleted list. diff --git a/packages/eslint-plugin-rax-runtime-miniapp/docs/rules/no-spread-props.md b/packages/eslint-plugin-rax-runtime-miniapp/docs/rules/no-spread-props.md deleted file mode 100644 index b78e167b..00000000 --- a/packages/eslint-plugin-rax-runtime-miniapp/docs/rules/no-spread-props.md +++ /dev/null @@ -1,35 +0,0 @@ -# 不推荐使用 {..props} 的方式传参 (no-spread-props) - -Please describe the origin of the rule here. - -## Rule Details - -This rule aims to... - -Examples of **incorrect** code for this rule: - -```js - -// fill me in - -``` - -Examples of **correct** code for this rule: - -```js - -// fill me in - -``` - -### Options - -If there are any options, describe them here. Otherwise, delete this section. - -## When Not To Use It - -Give a short description of when it would be appropriate to turn off this rule. - -## Further Reading - -If there are other links that describe the issue this rule addresses, please include them here in a bulleted list. diff --git a/packages/eslint-plugin-rax-runtime-miniapp/package.json b/packages/eslint-plugin-rax-runtime-miniapp/package.json index 2bfa04d4..70210397 100644 --- a/packages/eslint-plugin-rax-runtime-miniapp/package.json +++ b/packages/eslint-plugin-rax-runtime-miniapp/package.json @@ -1,6 +1,6 @@ { "name": "eslint-plugin-rax-runtime-miniapp", - "version": "0.0.1-0", + "version": "1.0.0", "description": "rax 运行时 eslint 插件", "keywords": [ "eslint", @@ -8,7 +8,7 @@ "eslint-plugin" ], "author": "chenrongyan", - "main": "lib/index.js", + "main": "src/index.js", "scripts": { "lint": "eslint .", "test": "mocha tests --recursive" diff --git a/packages/eslint-plugin-rax-runtime-miniapp/src/rules/no-spread-props.js b/packages/eslint-plugin-rax-runtime-miniapp/src/rules/no-spread-props.js deleted file mode 100644 index 848cb2da..00000000 --- a/packages/eslint-plugin-rax-runtime-miniapp/src/rules/no-spread-props.js +++ /dev/null @@ -1,48 +0,0 @@ -/** - * @fileoverview 不推荐使用 {..props} 的方式传参 - * @author chenrongyan - */ -"use strict"; - -const util = require('util'); -const docUrl = require('../utils/docUrl'); - -module.exports = { - meta: { - type: 'suggestion', - docs: { - description: "{...props} in runtime miniapp is not recommended", - recommended: false, - url: docUrl('no-spread-props'), - }, - fixable: null, // Or `code` or `whitespace` - schema: [], // Add a schema if the rule has options - }, - - create(context) { - const objectExpressions = []; - function reportSpreadProps(spreadProps) { - if (spreadProps) { - spreadProps.forEach(prop => { - console.log(prop); - if (prop) { - const expression = util.inspect(prop.expression); - context.report({ - node: prop.node, - message: 'Spread style: {{ expression }}', - data: { expression } - }) - } - }) - } - } - - return { - JSXSpreadAttribute: (node) => { - objectExpressions.push(node); - }, - - 'Program:exit': () => reportSpreadProps(objectExpressions) - }; - }, -}; diff --git a/packages/eslint-plugin-rax-runtime-miniapp/src/utils/index.js b/packages/eslint-plugin-rax-runtime-miniapp/src/utils/index.js new file mode 100644 index 00000000..5a882829 --- /dev/null +++ b/packages/eslint-plugin-rax-runtime-miniapp/src/utils/index.js @@ -0,0 +1,354 @@ +'use strict'; + +/** + * StyleSheets represents the StyleSheets found in the source code. + * @constructor + */ +function StyleSheets() { + this.styleSheets = {}; +} + +/** + * AddObjectexpressions adds an array of expressions to the ObjectExpressions collection + * @param {Array} expressions - an array of expressions containing ObjectExpressions in + * inline styles + */ +StyleSheets.prototype.addObjectExpressions = function (expressions) { + if (!this.objectExpressions) { + this.objectExpressions = []; + } + this.objectExpressions = this.objectExpressions.concat(expressions); +}; + +/** + * GetObjectExpressions returns an array of collected object expressiosn used in inline styles + * @returns {Array} + */ +StyleSheets.prototype.getObjectExpressions = function () { + return this.objectExpressions; +}; + + +let currentContent; +const getSourceCode = (node) => currentContent + .getSourceCode(node) + .getText(node); + +const getStyleSheetObjectNames = (settings) => settings['react-native/style-sheet-object-names'] || ['StyleSheet']; + +const astHelpers = { + containsStyleSheetObject: function (node, objectNames) { + return Boolean( + node + && node.type === 'CallExpression' + && node.callee + && node.callee.object + && node.callee.object.name + && objectNames.includes(node.callee.object.name) + ); + }, + + containsCreateCall: function (node) { + return Boolean( + node + && node.callee + && node.callee.property + && node.callee.property.name === 'create' + ); + }, + + isStyleSheetDeclaration: function (node, settings) { + const objectNames = getStyleSheetObjectNames(settings); + + return Boolean( + astHelpers.containsStyleSheetObject(node, objectNames) + && astHelpers.containsCreateCall(node) + ); + }, + + getStyleSheetName: function (node) { + if (node && node.parent && node.parent.id) { + return node.parent.id.name; + } + }, + + getStyleDeclarations: function (node) { + if ( + node + && node.type === 'CallExpression' + && node.arguments + && node.arguments[0] + && node.arguments[0].properties + ) { + return node.arguments[0].properties.filter((property) => property.type === 'Property'); + } + + return []; + }, + + getStyleDeclarationsChunks: function (node) { + if ( + node + && node.type === 'CallExpression' + && node.arguments + && node.arguments[0] + && node.arguments[0].properties + ) { + const { properties } = node.arguments[0]; + + const result = []; + let chunk = []; + for (let i = 0; i < properties.length; i += 1) { + const property = properties[i]; + if (property.type === 'Property') { + chunk.push(property); + } else if (chunk.length) { + result.push(chunk); + chunk = []; + } + } + if (chunk.length) { + result.push(chunk); + } + return result; + } + + return []; + }, + + getPropertiesChunks: function (properties) { + const result = []; + let chunk = []; + for (let i = 0; i < properties.length; i += 1) { + const property = properties[i]; + if (property.type === 'Property') { + chunk.push(property); + } else if (chunk.length) { + result.push(chunk); + chunk = []; + } + } + if (chunk.length) { + result.push(chunk); + } + return result; + }, + + getExpressionIdentifier: function (node) { + if (node) { + switch (node.type) { + case 'Identifier': + return node.name; + case 'Literal': + return node.value; + case 'TemplateLiteral': + return node.quasis.reduce((result, quasi, index) => result + + quasi.value.cooked + + astHelpers.getExpressionIdentifier(node.expressions[index]), + ''); + default: + return ''; + } + } + + return ''; + }, + + getStylePropertyIdentifier: function (node) { + if ( + node + && node.key + ) { + return astHelpers.getExpressionIdentifier(node.key); + } + }, + + isStyleAttribute: function (node) { + return Boolean( + node.type === 'JSXAttribute' + && node.name + && node.name.name + && node.name.name.toLowerCase().includes('style') + ); + }, + + collectStyleObjectExpressions: function (node, context) { + currentContent = context; + if (astHelpers.hasArrayOfStyleReferences(node)) { + const styleReferenceContainers = node + .expression + .elements; + + return astHelpers.collectStyleObjectExpressionFromContainers( + styleReferenceContainers + ); + } if (node && node.expression) { + return astHelpers.getStyleObjectExpressionFromNode(node.expression); + } + + return []; + }, + + collectStyleObjectExpressionFromContainers: function (nodes) { + let objectExpressions = []; + nodes.forEach((node) => { + objectExpressions = objectExpressions + .concat(astHelpers.getStyleObjectExpressionFromNode(node)); + }); + + return objectExpressions; + }, + + getStyleReferenceFromNode: function (node) { + let styleReference; + let leftStyleReferences; + let rightStyleReferences; + + if (!node) { + return []; + } + + switch (node.type) { + case 'MemberExpression': + styleReference = astHelpers.getStyleReferenceFromExpression(node); + return [styleReference]; + case 'LogicalExpression': + leftStyleReferences = astHelpers.getStyleReferenceFromNode(node.left); + rightStyleReferences = astHelpers.getStyleReferenceFromNode(node.right); + return [].concat(leftStyleReferences).concat(rightStyleReferences); + case 'ConditionalExpression': + leftStyleReferences = astHelpers.getStyleReferenceFromNode(node.consequent); + rightStyleReferences = astHelpers.getStyleReferenceFromNode(node.alternate); + return [].concat(leftStyleReferences).concat(rightStyleReferences); + default: + return []; + } + }, + + getStyleObjectExpressionFromNode: function (node) { + let leftStyleObjectExpression; + let rightStyleObjectExpression; + + if (!node) { + return []; + } + + if (node.type === 'ObjectExpression') { + return [astHelpers.getStyleObjectFromExpression(node)]; + } + + switch (node.type) { + case 'LogicalExpression': + leftStyleObjectExpression = astHelpers.getStyleObjectExpressionFromNode(node.left); + rightStyleObjectExpression = astHelpers.getStyleObjectExpressionFromNode(node.right); + return [].concat(leftStyleObjectExpression).concat(rightStyleObjectExpression); + case 'ConditionalExpression': + leftStyleObjectExpression = astHelpers.getStyleObjectExpressionFromNode(node.consequent); + rightStyleObjectExpression = astHelpers.getStyleObjectExpressionFromNode(node.alternate); + return [].concat(leftStyleObjectExpression).concat(rightStyleObjectExpression); + default: + return []; + } + }, + + hasArrayOfStyleReferences: function (node) { + return node && Boolean( + node.type === 'JSXExpressionContainer' + && node.expression + && node.expression.type === 'ArrayExpression' + ); + }, + + getStyleReferenceFromExpression: function (node) { + const result = []; + const name = astHelpers.getObjectName(node); + if (name) { + result.push(name); + } + + const property = astHelpers.getPropertyName(node); + if (property) { + result.push(property); + } + + return result.join('.'); + }, + + getStyleObjectFromExpression: function (node) { + const obj = {}; + let invalid = false; + if (node.properties && node.properties.length) { + node.properties.forEach((p) => { + if (!p.value || !p.key) { + return; + } + if (p.value.type === 'Literal') { + invalid = true; + obj[p.key.name] = p.value.value; + } else if (p.value.type === 'ConditionalExpression') { + const innerNode = p.value; + if (innerNode.consequent.type === 'Literal' || innerNode.alternate.type === 'Literal') { + invalid = true; + obj[p.key.name] = getSourceCode(innerNode); + } + } else if (p.value.type === 'UnaryExpression' && p.value.operator === '-' && p.value.argument.type === 'Literal') { + invalid = true; + obj[p.key.name] = -1 * p.value.argument.value; + } else if (p.value.type === 'UnaryExpression' && p.value.operator === '+' && p.value.argument.type === 'Literal') { + invalid = true; + obj[p.key.name] = p.value.argument.value; + } + }); + } + return invalid ? { expression: obj, node: node } : undefined; + }, + + + getObjectName: function (node) { + if ( + node + && node.object + && node.object.name + ) { + return node.object.name; + } + }, + + getPropertyName: function (node) { + if ( + node + && node.property + && node.property.name + ) { + return node.property.name; + } + }, + + getPotentialStyleReferenceFromMemberExpression: function (node) { + if ( + node + && node.object + && node.object.type === 'Identifier' + && node.object.name + && node.property + && node.property.type === 'Identifier' + && node.property.name + && node.parent.type !== 'MemberExpression' + ) { + return [node.object.name, node.property.name].join('.'); + } + }, + + isEitherShortHand: function (property1, property2) { + const shorthands = ['margin', 'padding', 'border', 'flex']; + if (shorthands.includes(property1)) { + return property2.startsWith(property1); + } if (shorthands.includes(property2)) { + return property1.startsWith(property2); + } + return false; + }, +}; + +module.exports.astHelpers = astHelpers; +module.exports.StyleSheets = StyleSheets; diff --git a/packages/eslint-plugin-rax-runtime-miniapp/src/utils/props.js b/packages/eslint-plugin-rax-runtime-miniapp/src/utils/props.js deleted file mode 100644 index eb109abb..00000000 --- a/packages/eslint-plugin-rax-runtime-miniapp/src/utils/props.js +++ /dev/null @@ -1,2 +0,0 @@ -'use strict'; - diff --git a/packages/eslint-plugin-rax-runtime-miniapp/src/utils/stylesheet.js b/packages/eslint-plugin-rax-runtime-miniapp/src/utils/stylesheet.js index 40ea13c4..0c4e8f77 100644 --- a/packages/eslint-plugin-rax-runtime-miniapp/src/utils/stylesheet.js +++ b/packages/eslint-plugin-rax-runtime-miniapp/src/utils/stylesheet.js @@ -8,63 +8,6 @@ function StyleSheets() { this.styleSheets = {}; } -/** - * Add adds a StyleSheet to our StyleSheets collections. - * - * @param {string} styleSheetName - The name of the StyleSheet. - * @param {object} properties - The collection of rules in the styleSheet. - */ -StyleSheets.prototype.add = function (styleSheetName, properties) { - this.styleSheets[styleSheetName] = properties; -}; - -/** - * MarkAsUsed marks a rule as used in our source code by removing it from the - * specified StyleSheet rules. - * - * @param {string} fullyQualifiedName - The fully qualified name of the rule. - * for example 'styles.text' - */ -StyleSheets.prototype.markAsUsed = function (fullyQualifiedName) { - const nameSplit = fullyQualifiedName.split('.'); - const styleSheetName = nameSplit[0]; - const styleSheetProperty = nameSplit[1]; - - if (this.styleSheets[styleSheetName]) { - this.styleSheets[styleSheetName] = this - .styleSheets[styleSheetName] - .filter((property) => property.key.name !== styleSheetProperty); - } -}; - -/** - * GetUnusedReferences returns all collected StyleSheets and their - * unmarked rules. - */ -StyleSheets.prototype.getUnusedReferences = function () { - return this.styleSheets; -}; - -/** - * AddColorLiterals adds an array of expressions that contain color literals - * to the ColorLiterals collection - * @param {array} expressions - an array of expressions containing color literals - */ -StyleSheets.prototype.addColorLiterals = function (expressions) { - if (!this.colorLiterals) { - this.colorLiterals = []; - } - this.colorLiterals = this.colorLiterals.concat(expressions); -}; - -/** - * GetColorLiterals returns an array of collected color literals expressions - * @returns {Array} - */ -StyleSheets.prototype.getColorLiterals = function () { - return this.colorLiterals; -}; - /** * AddObjectexpressions adds an array of expressions to the ObjectExpressions collection * @param {Array} expressions - an array of expressions containing ObjectExpressions in @@ -85,141 +28,12 @@ StyleSheets.prototype.getObjectExpressions = function () { return this.objectExpressions; }; - let currentContent; const getSourceCode = (node) => currentContent .getSourceCode(node) .getText(node); -const getStyleSheetObjectNames = (settings) => settings['react-native/style-sheet-object-names'] || ['StyleSheet']; - const astHelpers = { - containsStyleSheetObject: function (node, objectNames) { - return Boolean( - node - && node.type === 'CallExpression' - && node.callee - && node.callee.object - && node.callee.object.name - && objectNames.includes(node.callee.object.name) - ); - }, - - containsCreateCall: function (node) { - return Boolean( - node - && node.callee - && node.callee.property - && node.callee.property.name === 'create' - ); - }, - - isStyleSheetDeclaration: function (node, settings) { - const objectNames = getStyleSheetObjectNames(settings); - - return Boolean( - astHelpers.containsStyleSheetObject(node, objectNames) - && astHelpers.containsCreateCall(node) - ); - }, - - getStyleSheetName: function (node) { - if (node && node.parent && node.parent.id) { - return node.parent.id.name; - } - }, - - getStyleDeclarations: function (node) { - if ( - node - && node.type === 'CallExpression' - && node.arguments - && node.arguments[0] - && node.arguments[0].properties - ) { - return node.arguments[0].properties.filter((property) => property.type === 'Property'); - } - - return []; - }, - - getStyleDeclarationsChunks: function (node) { - if ( - node - && node.type === 'CallExpression' - && node.arguments - && node.arguments[0] - && node.arguments[0].properties - ) { - const { properties } = node.arguments[0]; - - const result = []; - let chunk = []; - for (let i = 0; i < properties.length; i += 1) { - const property = properties[i]; - if (property.type === 'Property') { - chunk.push(property); - } else if (chunk.length) { - result.push(chunk); - chunk = []; - } - } - if (chunk.length) { - result.push(chunk); - } - return result; - } - - return []; - }, - - getPropertiesChunks: function (properties) { - const result = []; - let chunk = []; - for (let i = 0; i < properties.length; i += 1) { - const property = properties[i]; - if (property.type === 'Property') { - chunk.push(property); - } else if (chunk.length) { - result.push(chunk); - chunk = []; - } - } - if (chunk.length) { - result.push(chunk); - } - return result; - }, - - getExpressionIdentifier: function (node) { - if (node) { - switch (node.type) { - case 'Identifier': - return node.name; - case 'Literal': - return node.value; - case 'TemplateLiteral': - return node.quasis.reduce((result, quasi, index) => result - + quasi.value.cooked - + astHelpers.getExpressionIdentifier(node.expressions[index]), - ''); - default: - return ''; - } - } - - return ''; - }, - - getStylePropertyIdentifier: function (node) { - if ( - node - && node.key - ) { - return astHelpers.getExpressionIdentifier(node.key); - } - }, - isStyleAttribute: function (node) { return Boolean( node.type === 'JSXAttribute' @@ -228,6 +42,14 @@ const astHelpers = { && node.name.name.toLowerCase().includes('style') ); }, + + hasArrayOfStyleReferences: function (node) { + return node && Boolean( + node.type === 'JSXExpressionContainer' + && node.expression + && node.expression.type === 'ArrayExpression' + ); + }, collectStyleObjectExpressions: function (node, context) { currentContent = context; @@ -246,29 +68,6 @@ const astHelpers = { return []; }, - collectColorLiterals: function (node, context) { - if (!node) { - return []; - } - - currentContent = context; - if (astHelpers.hasArrayOfStyleReferences(node)) { - const styleReferenceContainers = node - .expression - .elements; - - return astHelpers.collectColorLiteralsFromContainers( - styleReferenceContainers - ); - } - - if (node.type === 'ObjectExpression') { - return astHelpers.getColorLiteralsFromNode(node); - } - - return astHelpers.getColorLiteralsFromNode(node.expression); - }, - collectStyleObjectExpressionFromContainers: function (nodes) { let objectExpressions = []; nodes.forEach((node) => { @@ -279,117 +78,6 @@ const astHelpers = { return objectExpressions; }, - collectColorLiteralsFromContainers: function (nodes) { - let colorLiterals = []; - nodes.forEach((node) => { - colorLiterals = colorLiterals - .concat(astHelpers.getColorLiteralsFromNode(node)); - }); - - return colorLiterals; - }, - - getStyleReferenceFromNode: function (node) { - let styleReference; - let leftStyleReferences; - let rightStyleReferences; - - if (!node) { - return []; - } - - switch (node.type) { - case 'MemberExpression': - styleReference = astHelpers.getStyleReferenceFromExpression(node); - return [styleReference]; - case 'LogicalExpression': - leftStyleReferences = astHelpers.getStyleReferenceFromNode(node.left); - rightStyleReferences = astHelpers.getStyleReferenceFromNode(node.right); - return [].concat(leftStyleReferences).concat(rightStyleReferences); - case 'ConditionalExpression': - leftStyleReferences = astHelpers.getStyleReferenceFromNode(node.consequent); - rightStyleReferences = astHelpers.getStyleReferenceFromNode(node.alternate); - return [].concat(leftStyleReferences).concat(rightStyleReferences); - default: - return []; - } - }, - - getStyleObjectExpressionFromNode: function (node) { - let leftStyleObjectExpression; - let rightStyleObjectExpression; - - if (!node) { - return []; - } - - if (node.type === 'ObjectExpression') { - return [astHelpers.getStyleObjectFromExpression(node)]; - } - - switch (node.type) { - case 'LogicalExpression': - leftStyleObjectExpression = astHelpers.getStyleObjectExpressionFromNode(node.left); - rightStyleObjectExpression = astHelpers.getStyleObjectExpressionFromNode(node.right); - return [].concat(leftStyleObjectExpression).concat(rightStyleObjectExpression); - case 'ConditionalExpression': - leftStyleObjectExpression = astHelpers.getStyleObjectExpressionFromNode(node.consequent); - rightStyleObjectExpression = astHelpers.getStyleObjectExpressionFromNode(node.alternate); - return [].concat(leftStyleObjectExpression).concat(rightStyleObjectExpression); - default: - return []; - } - }, - - getColorLiteralsFromNode: function (node) { - let leftColorLiterals; - let rightColorLiterals; - - if (!node) { - return []; - } - - if (node.type === 'ObjectExpression') { - return [astHelpers.getColorLiteralsFromExpression(node)]; - } - - switch (node.type) { - case 'LogicalExpression': - leftColorLiterals = astHelpers.getColorLiteralsFromNode(node.left); - rightColorLiterals = astHelpers.getColorLiteralsFromNode(node.right); - return [].concat(leftColorLiterals).concat(rightColorLiterals); - case 'ConditionalExpression': - leftColorLiterals = astHelpers.getColorLiteralsFromNode(node.consequent); - rightColorLiterals = astHelpers.getColorLiteralsFromNode(node.alternate); - return [].concat(leftColorLiterals).concat(rightColorLiterals); - default: - return []; - } - }, - - hasArrayOfStyleReferences: function (node) { - return node && Boolean( - node.type === 'JSXExpressionContainer' - && node.expression - && node.expression.type === 'ArrayExpression' - ); - }, - - getStyleReferenceFromExpression: function (node) { - const result = []; - const name = astHelpers.getObjectName(node); - if (name) { - result.push(name); - } - - const property = astHelpers.getPropertyName(node); - if (property) { - result.push(property); - } - - return result.join('.'); - }, - getStyleObjectFromExpression: function (node) { const obj = {}; let invalid = false; @@ -419,73 +107,32 @@ const astHelpers = { return invalid ? { expression: obj, node: node } : undefined; }, - getColorLiteralsFromExpression: function (node) { - const obj = {}; - let invalid = false; - if (node.properties && node.properties.length) { - node.properties.forEach((p) => { - if (p.key && p.key.name && p.key.name.toLowerCase().indexOf('color') !== -1) { - if (p.value.type === 'Literal') { - invalid = true; - obj[p.key.name] = p.value.value; - } else if (p.value.type === 'ConditionalExpression') { - const innerNode = p.value; - if (innerNode.consequent.type === 'Literal' || innerNode.alternate.type === 'Literal') { - invalid = true; - obj[p.key.name] = getSourceCode(innerNode); - } - } - } - }); - } - return invalid ? { expression: obj, node: node } : undefined; - }, - - getObjectName: function (node) { - if ( - node - && node.object - && node.object.name - ) { - return node.object.name; - } - }, + getStyleObjectExpressionFromNode: function (node) { + let leftStyleObjectExpression; + let rightStyleObjectExpression; - getPropertyName: function (node) { - if ( - node - && node.property - && node.property.name - ) { - return node.property.name; + if (!node) { + return []; } - }, - getPotentialStyleReferenceFromMemberExpression: function (node) { - if ( - node - && node.object - && node.object.type === 'Identifier' - && node.object.name - && node.property - && node.property.type === 'Identifier' - && node.property.name - && node.parent.type !== 'MemberExpression' - ) { - return [node.object.name, node.property.name].join('.'); + if (node.type === 'ObjectExpression') { + return [astHelpers.getStyleObjectFromExpression(node)]; } - }, - isEitherShortHand: function (property1, property2) { - const shorthands = ['margin', 'padding', 'border', 'flex']; - if (shorthands.includes(property1)) { - return property2.startsWith(property1); - } if (shorthands.includes(property2)) { - return property1.startsWith(property2); + switch (node.type) { + case 'LogicalExpression': + leftStyleObjectExpression = astHelpers.getStyleObjectExpressionFromNode(node.left); + rightStyleObjectExpression = astHelpers.getStyleObjectExpressionFromNode(node.right); + return [].concat(leftStyleObjectExpression).concat(rightStyleObjectExpression); + case 'ConditionalExpression': + leftStyleObjectExpression = astHelpers.getStyleObjectExpressionFromNode(node.consequent); + rightStyleObjectExpression = astHelpers.getStyleObjectExpressionFromNode(node.alternate); + return [].concat(leftStyleObjectExpression).concat(rightStyleObjectExpression); + default: + return []; } - return false; }, -}; +} module.exports.astHelpers = astHelpers; -module.exports.StyleSheets = StyleSheets; +module.exports.StyleSheets = StyleSheets; \ No newline at end of file diff --git a/packages/eslint-plugin-rax-runtime-miniapp/tests/src/rules/no-spread-props.js b/packages/eslint-plugin-rax-runtime-miniapp/tests/src/rules/no-spread-props.js deleted file mode 100644 index abbaad1d..00000000 --- a/packages/eslint-plugin-rax-runtime-miniapp/tests/src/rules/no-spread-props.js +++ /dev/null @@ -1,34 +0,0 @@ -/** - * @fileoverview 不推荐使用 {..props} 的方式传参 - * @author chenrongyan - */ -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require("../../../src/rules/no-inline-styles"), - RuleTester = require("eslint").RuleTester; - -const ruleTester = new RuleTester({ - parserOptions: { - ecmaVersion: 10, - ecmaFeatures: { - jsx: true, - }, - }, -}); - -ruleTester.run("no-spread-props", rule, { - valid: [ - // give me some code that won't trigger a warning - ], - - invalid: [ - { - code: "", - errors: [{ message: "Fill me in.", type: "Me too" }], - }, - ], -}); From 88b13576da82325cdf9abd45890986eb50e956f1 Mon Sep 17 00:00:00 2001 From: "chenrongyan.cry" Date: Mon, 23 Aug 2021 16:44:27 +0800 Subject: [PATCH 3/4] fix --- .../eslint-plugin-rax-runtime-miniapp/src/index.js | 11 +---------- .../src/utils/docUrl.js | 2 +- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/packages/eslint-plugin-rax-runtime-miniapp/src/index.js b/packages/eslint-plugin-rax-runtime-miniapp/src/index.js index 4ccdf608..6f4d9e62 100644 --- a/packages/eslint-plugin-rax-runtime-miniapp/src/index.js +++ b/packages/eslint-plugin-rax-runtime-miniapp/src/index.js @@ -4,16 +4,7 @@ */ "use strict"; -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - const requireIndex = require("requireindex"); -//------------------------------------------------------------------------------ -// Plugin Definition -//------------------------------------------------------------------------------ - - -// import all rules in lib/rules +// import all rules in src/rules module.exports.rules = requireIndex(__dirname + "/rules"); diff --git a/packages/eslint-plugin-rax-runtime-miniapp/src/utils/docUrl.js b/packages/eslint-plugin-rax-runtime-miniapp/src/utils/docUrl.js index ef638a9f..9c4f5a88 100644 --- a/packages/eslint-plugin-rax-runtime-miniapp/src/utils/docUrl.js +++ b/packages/eslint-plugin-rax-runtime-miniapp/src/utils/docUrl.js @@ -4,4 +4,4 @@ module.exports = function docUrl(docName) { const repoUrl = 'https://github.com/raxjs/miniapp/tree/master/packages/eslint-plugin-rax-runtime-miniapp'; return `${repoUrl}/docs/rules/${docName}.md`; -} \ No newline at end of file +} From 28c8d8ea382c560641a00eac6ed93b8e7c1e6d5f Mon Sep 17 00:00:00 2001 From: "chenrongyan.cry" Date: Mon, 23 Aug 2021 16:45:16 +0800 Subject: [PATCH 4/4] fix --- .../src/utils/index.js | 354 ------------------ 1 file changed, 354 deletions(-) delete mode 100644 packages/eslint-plugin-rax-runtime-miniapp/src/utils/index.js diff --git a/packages/eslint-plugin-rax-runtime-miniapp/src/utils/index.js b/packages/eslint-plugin-rax-runtime-miniapp/src/utils/index.js deleted file mode 100644 index 5a882829..00000000 --- a/packages/eslint-plugin-rax-runtime-miniapp/src/utils/index.js +++ /dev/null @@ -1,354 +0,0 @@ -'use strict'; - -/** - * StyleSheets represents the StyleSheets found in the source code. - * @constructor - */ -function StyleSheets() { - this.styleSheets = {}; -} - -/** - * AddObjectexpressions adds an array of expressions to the ObjectExpressions collection - * @param {Array} expressions - an array of expressions containing ObjectExpressions in - * inline styles - */ -StyleSheets.prototype.addObjectExpressions = function (expressions) { - if (!this.objectExpressions) { - this.objectExpressions = []; - } - this.objectExpressions = this.objectExpressions.concat(expressions); -}; - -/** - * GetObjectExpressions returns an array of collected object expressiosn used in inline styles - * @returns {Array} - */ -StyleSheets.prototype.getObjectExpressions = function () { - return this.objectExpressions; -}; - - -let currentContent; -const getSourceCode = (node) => currentContent - .getSourceCode(node) - .getText(node); - -const getStyleSheetObjectNames = (settings) => settings['react-native/style-sheet-object-names'] || ['StyleSheet']; - -const astHelpers = { - containsStyleSheetObject: function (node, objectNames) { - return Boolean( - node - && node.type === 'CallExpression' - && node.callee - && node.callee.object - && node.callee.object.name - && objectNames.includes(node.callee.object.name) - ); - }, - - containsCreateCall: function (node) { - return Boolean( - node - && node.callee - && node.callee.property - && node.callee.property.name === 'create' - ); - }, - - isStyleSheetDeclaration: function (node, settings) { - const objectNames = getStyleSheetObjectNames(settings); - - return Boolean( - astHelpers.containsStyleSheetObject(node, objectNames) - && astHelpers.containsCreateCall(node) - ); - }, - - getStyleSheetName: function (node) { - if (node && node.parent && node.parent.id) { - return node.parent.id.name; - } - }, - - getStyleDeclarations: function (node) { - if ( - node - && node.type === 'CallExpression' - && node.arguments - && node.arguments[0] - && node.arguments[0].properties - ) { - return node.arguments[0].properties.filter((property) => property.type === 'Property'); - } - - return []; - }, - - getStyleDeclarationsChunks: function (node) { - if ( - node - && node.type === 'CallExpression' - && node.arguments - && node.arguments[0] - && node.arguments[0].properties - ) { - const { properties } = node.arguments[0]; - - const result = []; - let chunk = []; - for (let i = 0; i < properties.length; i += 1) { - const property = properties[i]; - if (property.type === 'Property') { - chunk.push(property); - } else if (chunk.length) { - result.push(chunk); - chunk = []; - } - } - if (chunk.length) { - result.push(chunk); - } - return result; - } - - return []; - }, - - getPropertiesChunks: function (properties) { - const result = []; - let chunk = []; - for (let i = 0; i < properties.length; i += 1) { - const property = properties[i]; - if (property.type === 'Property') { - chunk.push(property); - } else if (chunk.length) { - result.push(chunk); - chunk = []; - } - } - if (chunk.length) { - result.push(chunk); - } - return result; - }, - - getExpressionIdentifier: function (node) { - if (node) { - switch (node.type) { - case 'Identifier': - return node.name; - case 'Literal': - return node.value; - case 'TemplateLiteral': - return node.quasis.reduce((result, quasi, index) => result - + quasi.value.cooked - + astHelpers.getExpressionIdentifier(node.expressions[index]), - ''); - default: - return ''; - } - } - - return ''; - }, - - getStylePropertyIdentifier: function (node) { - if ( - node - && node.key - ) { - return astHelpers.getExpressionIdentifier(node.key); - } - }, - - isStyleAttribute: function (node) { - return Boolean( - node.type === 'JSXAttribute' - && node.name - && node.name.name - && node.name.name.toLowerCase().includes('style') - ); - }, - - collectStyleObjectExpressions: function (node, context) { - currentContent = context; - if (astHelpers.hasArrayOfStyleReferences(node)) { - const styleReferenceContainers = node - .expression - .elements; - - return astHelpers.collectStyleObjectExpressionFromContainers( - styleReferenceContainers - ); - } if (node && node.expression) { - return astHelpers.getStyleObjectExpressionFromNode(node.expression); - } - - return []; - }, - - collectStyleObjectExpressionFromContainers: function (nodes) { - let objectExpressions = []; - nodes.forEach((node) => { - objectExpressions = objectExpressions - .concat(astHelpers.getStyleObjectExpressionFromNode(node)); - }); - - return objectExpressions; - }, - - getStyleReferenceFromNode: function (node) { - let styleReference; - let leftStyleReferences; - let rightStyleReferences; - - if (!node) { - return []; - } - - switch (node.type) { - case 'MemberExpression': - styleReference = astHelpers.getStyleReferenceFromExpression(node); - return [styleReference]; - case 'LogicalExpression': - leftStyleReferences = astHelpers.getStyleReferenceFromNode(node.left); - rightStyleReferences = astHelpers.getStyleReferenceFromNode(node.right); - return [].concat(leftStyleReferences).concat(rightStyleReferences); - case 'ConditionalExpression': - leftStyleReferences = astHelpers.getStyleReferenceFromNode(node.consequent); - rightStyleReferences = astHelpers.getStyleReferenceFromNode(node.alternate); - return [].concat(leftStyleReferences).concat(rightStyleReferences); - default: - return []; - } - }, - - getStyleObjectExpressionFromNode: function (node) { - let leftStyleObjectExpression; - let rightStyleObjectExpression; - - if (!node) { - return []; - } - - if (node.type === 'ObjectExpression') { - return [astHelpers.getStyleObjectFromExpression(node)]; - } - - switch (node.type) { - case 'LogicalExpression': - leftStyleObjectExpression = astHelpers.getStyleObjectExpressionFromNode(node.left); - rightStyleObjectExpression = astHelpers.getStyleObjectExpressionFromNode(node.right); - return [].concat(leftStyleObjectExpression).concat(rightStyleObjectExpression); - case 'ConditionalExpression': - leftStyleObjectExpression = astHelpers.getStyleObjectExpressionFromNode(node.consequent); - rightStyleObjectExpression = astHelpers.getStyleObjectExpressionFromNode(node.alternate); - return [].concat(leftStyleObjectExpression).concat(rightStyleObjectExpression); - default: - return []; - } - }, - - hasArrayOfStyleReferences: function (node) { - return node && Boolean( - node.type === 'JSXExpressionContainer' - && node.expression - && node.expression.type === 'ArrayExpression' - ); - }, - - getStyleReferenceFromExpression: function (node) { - const result = []; - const name = astHelpers.getObjectName(node); - if (name) { - result.push(name); - } - - const property = astHelpers.getPropertyName(node); - if (property) { - result.push(property); - } - - return result.join('.'); - }, - - getStyleObjectFromExpression: function (node) { - const obj = {}; - let invalid = false; - if (node.properties && node.properties.length) { - node.properties.forEach((p) => { - if (!p.value || !p.key) { - return; - } - if (p.value.type === 'Literal') { - invalid = true; - obj[p.key.name] = p.value.value; - } else if (p.value.type === 'ConditionalExpression') { - const innerNode = p.value; - if (innerNode.consequent.type === 'Literal' || innerNode.alternate.type === 'Literal') { - invalid = true; - obj[p.key.name] = getSourceCode(innerNode); - } - } else if (p.value.type === 'UnaryExpression' && p.value.operator === '-' && p.value.argument.type === 'Literal') { - invalid = true; - obj[p.key.name] = -1 * p.value.argument.value; - } else if (p.value.type === 'UnaryExpression' && p.value.operator === '+' && p.value.argument.type === 'Literal') { - invalid = true; - obj[p.key.name] = p.value.argument.value; - } - }); - } - return invalid ? { expression: obj, node: node } : undefined; - }, - - - getObjectName: function (node) { - if ( - node - && node.object - && node.object.name - ) { - return node.object.name; - } - }, - - getPropertyName: function (node) { - if ( - node - && node.property - && node.property.name - ) { - return node.property.name; - } - }, - - getPotentialStyleReferenceFromMemberExpression: function (node) { - if ( - node - && node.object - && node.object.type === 'Identifier' - && node.object.name - && node.property - && node.property.type === 'Identifier' - && node.property.name - && node.parent.type !== 'MemberExpression' - ) { - return [node.object.name, node.property.name].join('.'); - } - }, - - isEitherShortHand: function (property1, property2) { - const shorthands = ['margin', 'padding', 'border', 'flex']; - if (shorthands.includes(property1)) { - return property2.startsWith(property1); - } if (shorthands.includes(property2)) { - return property1.startsWith(property2); - } - return false; - }, -}; - -module.exports.astHelpers = astHelpers; -module.exports.StyleSheets = StyleSheets;