使用ESLint做ReactNative的代码检查

使用ESLint做ReactNative的代码检查

效果如下:

官网:https://github.com/Intellicode/eslint-plugin-react-native

任意目录

1.安装eslint

npm install eslint

如果不行 绪执行全局命令

npm install -g eslint

 

2.安装eslint-plugin-react

npm install eslint-plugin-react

 

3.安装eslint-plugin-react-native

npm install eslint-plugin-react-native

安装后的目

/Users/当前用/platform/node_modules/eslint

4.在工程下增加配置文件

  eslint向模式:

eslint --init

行完后会生成 .eslintrc.js

5.编辑配置文件 .eslintrc.js

vi .eslintrc.js

按照官方文档配置:

增加plugins,增加react-native规则

module.exports = {

    "env": {

        "browser": false,

        "commonjs": true,

        "es6": true,

        "node": true

    },

    "extends": ["eslint:recommended","plugin:react/recommended"],

    "parserOptions": {

        "ecmaFeatures": {

            "experimentalObjectRestSpread": true,

            "jsx": true

        },

        "sourceType": "module"

    },

    "plugins": [

        "react",

        "react-native"

    ],

    "rules": {

        "indent": [

            "error",

            4

        ],

        "linebreak-style": [

            "error",

            "unix"

        ],

        "quotes": [

            "error",

            "single"

        ],

        "semi": [

            "error",

            "always"

        ],

        "react-native/no-unused-styles": 2,

         "react-native/split-platform-components": 2

    }

};

 6.覆盖

/Users/purple/platform/node_modules/eslint/lib/rules/ no-unused-vars.js

/**

* @fileoverview Rule to flag declared but unused variables

* @author Ilya Volodin

*/

"use strict";

//------------------------------------------------------------------------------

// Requirements

//------------------------------------------------------------------------------

var lodash = require("lodash");

//------------------------------------------------------------------------------

// Rule Definition

//------------------------------------------------------------------------------

module.exports = function(context) {

    var MESSAGE = "'{{name}}' is defined but never used";

    var config = {

        vars: "all",

        args: "after-used"

    };

    var firstOption = context.options[0];

    if (firstOption) {

        if (typeof firstOption === "string") {

            config.vars = firstOption;

        } else {

            config.vars = firstOption.vars || config.vars;

            config.args = firstOption.args || config.args;

            if (firstOption.varsIgnorePattern) {

                config.varsIgnorePattern = new RegExp(firstOption.varsIgnorePattern);

            }

            if (firstOption.argsIgnorePattern) {

                config.argsIgnorePattern = new RegExp(firstOption.argsIgnorePattern);

            }

        }

    }

    //--------------------------------------------------------------------------

    // Helpers

    //--------------------------------------------------------------------------

    /**

     * Determines if a given variable is being exported from a module.

     * @param {Variable} variable - EScope variable object.

     * @returns {boolean} True if the variable is exported, false if not.

     * @private

     */

    function isExported(variable) {

        var definition = variable.defs[0];

        if (definition) {

            var node = definition.node;

            if (node.type === "VariableDeclarator") {

                node = node.parent;

            } else if (definition.type === "Parameter") {

                return false;

            }

            return node.parent.type.indexOf("Export") === 0;

        } else {

            return false;

        }

    }

    /**

     * Determines if a reference is a read operation.

     * @param {Reference} ref - An escope Reference

     * @returns {Boolean} whether the given reference represents a read operation

     * @private

     */

    function isReadRef(ref) {

        return ref.isRead();

    }

    /**

     * Determine if an identifier is referencing an enclosing function name.

     * @param {Reference} ref - The reference to check.

     * @param {ASTNode[]} nodes - The candidate function nodes.

     * @returns {boolean} True if it's a self-reference, false if not.

     * @private

     */

    function isSelfReference(ref, nodes) {

        var scope = ref.from;

        while (scope) {

            if (nodes.indexOf(scope.block) >= 0) {

                return true;

            }

            scope = scope.upper;

        }

        return false;

    }

    /**

     * Determines if the variable is used.

     * @param {Variable} variable - The variable to check.

     * @param {Reference[]} references - The variable references to check.

     * @returns {boolean} True if the variable is used

     */

    function isUsedVariable(variable) {

        var functionNodes = variable.defs.filter(function(def) {

                return def.type === "FunctionName";

            }).map(function(def) {

                return def.node;

            }),

            isFunctionDefinition = functionNodes.length > 0;

        return variable.references.some(function(ref) {

            return isReadRef(ref) && !(isFunctionDefinition && isSelfReference(ref, functionNodes));

        });

    }

    /**

     * Gets an array of variables without read references.

     * @param {Scope} scope - an escope Scope object.

     * @param {Variable[]} unusedVars - an array that saving result.

     * @returns {Variable[]} unused variables of the scope and descendant scopes.

     * @private

     */

    function collectUnusedVariables(scope, unusedVars) {

        var variables = scope.variables;

        var childScopes = scope.childScopes;

        var i, l;

        if (scope.type !== "TDZ" && (scope.type !== "global" || config.vars === "all")) {

            for (i = 0, l = variables.length; i < l; ++i) {

                var variable = variables[i];

                // skip a variable of class itself name in the class scope

                if (scope.type === "class" && scope.block.id === variable.identifiers[0]) {

                    continue;

                }

                // skip function expression names and variables marked with markVariableAsUsed()

                if (scope.functionExpressionScope || variable.eslintUsed  || variables[i].eslintJSXUsed) {

                    continue;

                }

                // skip implicit "arguments" variable

                if (scope.type === "function" && variable.name === "arguments" && variable.identifiers.length === 0) {

                    continue;

                }

                // explicit global variables don't have definitions.

                var def = variable.defs[0];

                if (def) {

                    var type = def.type;

                    // skip catch variables

                    if (type === "CatchClause") {

                        continue;

                    }

                    if (type === "Parameter") {

                        // skip any setter argument

                        if (def.node.parent.type === "Property" && def.node.parent.kind === "set") {

                            continue;

                        }

                        // if "args" option is "none", skip any parameter

                        if (config.args === "none") {

                            continue;

                        }

                        // skip ignored parameters

                        if (config.argsIgnorePattern && config.argsIgnorePattern.test(def.name.name)) {

                            continue;

                        }

                        // if "args" option is "after-used", skip all but the last parameter

                        if (config.args === "after-used" && def.index < def.node.params.length - 1) {

                            continue;

                        }

                    } else {

                        // skip ignored variables

                        if (config.varsIgnorePattern && config.varsIgnorePattern.test(def.name.name)) {

                            continue;

                        }

                    }

                }

                if (!isUsedVariable(variable) && !isExported(variable)) {

                    unusedVars.push(variable);

                }

            }

        }

        for (i = 0, l = childScopes.length; i < l; ++i) {

            collectUnusedVariables(childScopes[i], unusedVars);

        }

        return unusedVars;

    }

    /**

     * Gets the index of a given variable name in a given comment.

     * @param {escope.Variable} variable - A variable to get.

     * @param {ASTNode} comment - A comment node which includes the variable name.

     * @returns {number} The index of the variable name's location.

     */

    function getColumnInComment(variable, comment) {

        var namePattern = new RegExp("[\\s,]" + lodash.escapeRegExp(variable.name) + "(?:$|[\\s,:])", "g");

        // To ignore the first text "global".

        namePattern.lastIndex = comment.value.indexOf("global") + 6;

        // Search a given variable name.

        var match = namePattern.exec(comment.value);

        return match ? match.index + 1 : 0;

    }

    /**

     * Creates the correct location of a given variables.

     * The location is at its name string in a `/*global` comment.

     *

     * @param {escope.Variable} variable - A variable to get its location.

     * @returns {{line: number, column: number}} The location object for the variable.

     */

    function getLocation(variable) {

        var comment = variable.eslintExplicitGlobalComment;

        var baseLoc = comment.loc.start;

        var column = getColumnInComment(variable, comment);

        var prefix = comment.value.slice(0, column);

        var lineInComment = (prefix.match(/\n/g) || []).length;

        if (lineInComment > 0) {

            column -= 1 + prefix.lastIndexOf("\n");

        } else {

            // 2 is for `/*`

            column += baseLoc.column + 2;

        }

        return {

            line: baseLoc.line + lineInComment,

            column: column

        };

    }

    //--------------------------------------------------------------------------

    // Public

    //--------------------------------------------------------------------------

    return {

        "Program:exit": function(programNode) {

            var unusedVars = collectUnusedVariables(context.getScope(), []);

            for (var i = 0, l = unusedVars.length; i < l; ++i) {

                var unusedVar = unusedVars[i];

                if (unusedVar.eslintExplicitGlobal) {

                    context.report({

                        node: programNode,

                        loc: getLocation(unusedVar),

                        message: MESSAGE,

                        data: unusedVar

                    });

                } else if (unusedVar.defs.length > 0) {

                    context.report({

                        node: unusedVar.identifiers[0],

                        message: MESSAGE,

                        data: unusedVar

                    });

                }

            }

        },

      "XJSIdentifier": function(node) {

         var scope = context.getScope(),

             variables = scope.variables,

             i,

             len;

         // mark XJSIdentifiers with a special flag

         for (i = 0, len = variables.length; i < len; i++) {

             if (variables[i].name === node.name) {

                 variables[i].eslintJSXUsed = true;

                 break;

             }

         }

     }

    };

};

module.exports.schema = [

    {

        "oneOf": [

            {

                "enum": ["all", "local"]

            },

            {

                "type": "object",

                "properties": {

                    "vars": {

                        "enum": ["all", "local"]

                    },

                    "varsIgnorePattern": {

                        "type": "string"

                    },

                    "args": {

                        "enum": ["all", "after-used", "none"]

                    },

                    "argsIgnorePattern": {

                        "type": "string"

                    }

                }

            }

        ]

    }

];

 

主要是解决es6下模块导入检查的BUG

增加了XJSIdentifier方法修复。

7.执行命令检查

eslint index.ios.js

 

规则命令:

"no-alert": 0,//禁止使用alert confirm prompt

"no-array-constructor": 2,//禁止使用数组构造器

"no-bitwise": 0,//禁止使用按位运算符

"no-caller": 1,//禁止使用arguments.caller或arguments.callee

"no-catch-shadow": 2,//禁止catch子句参数与外部作用域变量同名

"no-class-assign": 2,//禁止给类赋值

"no-cond-assign": 2,//禁止在条件表达式中使用赋值语句

"no-console": 2,//禁止使用console

"no-const-assign": 2,//禁止修改const声明的变量

"no-constant-condition": 2,//禁止在条件中使用常量表达式 if(true) if(1)

"no-continue": 0,//禁止使用continue

"no-control-regex": 2,//禁止在正则表达式中使用控制字符

"no-debugger": 2,//禁止使用debugger

"no-delete-var": 2,//不能对var声明的变量使用delete操作符

"no-div-regex": 1,//不能使用看起来像除法的正则表达式/=foo/

"no-dupe-keys": 2,//在创建对象字面量时不允许键重复 {a:1,a:1}

"no-dupe-args": 2,//函数参数不能重复

"no-duplicate-case": 2,//switch中的case标签不能重复

"no-else-return": 2,//如果if语句里面有return,后面不能跟else语句

"no-empty": 2,//块语句中的内容不能为空

"no-empty-character-class": 2,//正则表达式中的[]内容不能为空

"no-empty-label": 2,//禁止使用空label

"no-eq-null": 2,//禁止对null使用==或!=运算符

"no-eval": 1,//禁止使用eval

"no-ex-assign": 2,//禁止给catch语句中的异常参数赋值

"no-extend-native": 2,//禁止扩展native对象

"no-extra-bind": 2,//禁止不必要的函数绑定

"no-extra-boolean-cast": 2,//禁止不必要的bool转换

"no-extra-parens": 2,//禁止非必要的括号

"no-extra-semi": 2,//禁止多余的冒号

"no-fallthrough": 1,//禁止switch穿透

"no-floating-decimal": 2,//禁止省略浮点数中的0 .5 3.

"no-func-assign": 2,//禁止重复的函数声明

"no-implicit-coercion": 1,//禁止隐式转换

"no-implied-eval": 2,//禁止使用隐式eval

"no-inline-comments": 0,//禁止行内备注

"no-inner-declarations": [2, "functions"],//禁止在块语句中使用声明(变量或函数)

"no-invalid-regexp": 2,//禁止无效的正则表达式

"no-invalid-this": 2,//禁止无效的this,只能用在构造器,类,对象字面量

"no-irregular-whitespace": 2,//不能有不规则的空格

"no-iterator": 2,//禁止使用__iterator__ 属性

"no-label-var": 2,//label名不能与var声明的变量名相同

"no-labels": 2,//禁止标签声明

"no-lone-blocks": 2,//禁止不必要的嵌套块

"no-lonely-if": 2,//禁止else语句内只有if语句

"no-loop-func": 1,//禁止在循环中使用函数(如果没有引用外部变量不形成闭包就可以)

"no-mixed-requires": [0, false],//声明时不能混用声明类型

"no-mixed-spaces-and-tabs": [2, false],//禁止混用tab和空格

"linebreak-style": [0, "windows"],//换行风格

"no-multi-spaces": 1,//不能用多余的空格

"no-multi-str": 2,//字符串不能用\换行

"no-multiple-empty-lines": [1, {"max": 2}],//空行最多不能超过2行

"no-native-reassign": 2,//不能重写native对象

"no-negated-in-lhs": 2,//in 操作符的左边不能有!

"no-nested-ternary": 0,//禁止使用嵌套的三目运算

"no-new": 1,//禁止在使用new构造一个实例后不赋值

"no-new-func": 1,//禁止使用new Function

"no-new-object": 2,//禁止使用new Object()

"no-new-require": 2,//禁止使用new require

"no-new-wrappers": 2,//禁止使用new创建包装实例,new String new Boolean new Number

"no-obj-calls": 2,//不能调用内置的全局对象,比如Math() JSON()

"no-octal": 2,//禁止使用八进制数字

"no-octal-escape": 2,//禁止使用八进制转义序列

"no-param-reassign": 2,//禁止给参数重新赋值

"no-path-concat": 0,//node中不能使用__dirname或__filename做路径拼接

"no-plusplus": 0,//禁止使用++,--

"no-process-env": 0,//禁止使用process.env

"no-process-exit": 0,//禁止使用process.exit()

"no-proto": 2,//禁止使用__proto__属性

"no-redeclare": 2,//禁止重复声明变量

"no-regex-spaces": 2,//禁止在正则表达式字面量中使用多个空格 /foo bar/

"no-restricted-modules": 0,//如果禁用了指定模块,使用就会报错

"no-return-assign": 1,//return 语句中不能有赋值表达式

"no-script-url": 0,//禁止使用javascript:void(0)

"no-self-compare": 2,//不能比较自身

"no-sequences": 0,//禁止使用逗号运算符

"no-shadow": 2,//外部作用域中的变量不能与它所包含的作用域中的变量或参数同名

"no-shadow-restricted-names": 2,//严格模式中规定的限制标识符不能作为声明时的变量名使用

"no-spaced-func": 2,//函数调用时 函数名与()之间不能有空格

"no-sparse-arrays": 2,//禁止稀疏数组, [1,,2]

"no-sync": 0,//nodejs 禁止同步方法

"no-ternary": 0,//禁止使用三目运算符

"no-trailing-spaces": 1,//一行结束后面不要有空格

"no-this-before-super": 0,//在调用super()之前不能使用this或super

"no-throw-literal": 2,//禁止抛出字面量错误 throw "error";

"no-undef": 1,//不能有未定义的变量

"no-undef-init": 2,//变量初始化时不能直接给它赋值为undefined

"no-undefined": 2,//不能使用undefined

"no-unexpected-multiline": 2,//避免多行表达式

"no-underscore-dangle": 1,//标识符不能以_开头或结尾

"no-unneeded-ternary": 2,//禁止不必要的嵌套 var isYes = answer === 1 ? true : false;

"no-unreachable": 2,//不能有无法执行的代码

"no-unused-expressions": 2,//禁止无用的表达式

"no-unused-vars": [2, {"vars": "all", "args": "after-used"}],//不能有声明后未被使用的变量或参数

"no-use-before-define": 2,//未定义前不能使用

"no-useless-call": 2,//禁止不必要的call和apply

"no-void": 2,//禁用void操作符

"no-var": 0,//禁用var,用let和const代替

"no-warning-comments": [1, { "terms": ["todo", "fixme", "xxx"], "location": "start" }],//不能有警告备注

"no-with": 2,//禁用with

"array-bracket-spacing": [2, "never"],//是否允许非空数组里面有多余的空格

"arrow-parens": 0,//箭头函数用小括号括起来

"arrow-spacing": 0,//=>的前/后括号

"accessor-pairs": 0,//在对象中使用getter/setter

"block-scoped-var": 0,//块语句中使用var

"brace-style": [1, "1tbs"],//大括号风格

"callback-return": 1,//避免多次调用回调什么的

"camelcase": 2,//强制驼峰法命名

"comma-dangle": [2, "never"],//对象字面量项尾不能有逗号

"comma-spacing": 0,//逗号前后的空格

"comma-style": [2, "last"],//逗号风格,换行时在行首还是行尾

"complexity": [0, 11],//循环复杂度

"computed-property-spacing": [0, "never"],//是否允许计算后的键名什么的

"consistent-return": 0,//return 后面是否允许省略

"consistent-this": [2, "that"],//this别名

"constructor-super": 0,//非派生类不能调用super,派生类必须调用super

"curly": [2, "all"],//必须使用 if(){} 中的{}

"default-case": 2,//switch语句最后必须有default

"dot-location": 0,//对象访问符的位置,换行的时候在行首还是行尾

"dot-notation": [0, { "allowKeywords": true }],//避免不必要的方括号

"eol-last": 0,//文件以单一的换行符结束

"eqeqeq": 2,//必须使用全等

"func-names": 0,//函数表达式必须有名字

"func-style": [0, "declaration"],//函数风格,规定只能使用函数声明/函数表达式

"generator-star-spacing": 0,//生成器函数*的前后空格

"guard-for-in": 0,//for in循环要用if语句过滤

"handle-callback-err": 0,//nodejs 处理错误

"id-length": 0,//变量名长度

"indent": [2, 4],//缩进风格

"init-declarations": 0,//声明时必须赋初值

"key-spacing": [0, { "beforeColon": false, "afterColon": true }],//对象字面量中冒号的前后空格

"lines-around-comment": 0,//行前/行后备注

"max-depth": [0, 4],//嵌套块深度

"max-len": [0, 80, 4],//字符串最大长度

"max-nested-callbacks": [0, 2],//回调嵌套深度

"max-params": [0, 3],//函数最多只能有3个参数

"max-statements": [0, 10],//函数内最多有几个声明

"new-cap": 2,//函数名首行大写必须使用new方式调用,首行小写必须用不带new方式调用

"new-parens": 2,//new时必须加小括号

"newline-after-var": 2,//变量声明后是否需要空一行

"object-curly-spacing": [0, "never"],//大括号内是否允许不必要的空格

"object-shorthand": 0,//强制对象字面量缩写语法

"one-var": 1,//连续声明

"operator-assignment": [0, "always"],//赋值运算符 += -=什么的

"operator-linebreak": [2, "after"],//换行时运算符在行尾还是行首

"padded-blocks": 0,//块语句内行首行尾是否要空行

"prefer-const": 0,//首选const

"prefer-spread": 0,//首选展开运算

"prefer-reflect": 0,//首选Reflect的方法

"quotes": [1, "single"],//引号类型 `` "" ''

"quote-props":[2, "always"],//对象字面量中的属性名是否强制双引号

"radix": 2,//parseInt必须指定第二个参数

"id-match": 0,//命名检测

"require-yield": 0,//生成器函数必须有yield

"semi": [2, "always"],//语句强制分号结尾

"semi-spacing": [0, {"before": false, "after": true}],//分号前后空格

"sort-vars": 0,//变量声明时排序

"space-after-keywords": [0, "always"],//关键字后面是否要空一格

"space-before-blocks": [0, "always"],//不以新行开始的块{前面要不要有空格

"space-before-function-paren": [0, "always"],//函数定义时括号前面要不要有空格

"space-in-parens": [0, "never"],//小括号里面要不要有空格

"space-infix-ops": 0,//中缀操作符周围要不要有空格

"space-return-throw-case": 2,//return throw case后面要不要加空格

"space-unary-ops": [0, { "words": true, "nonwords": false }],//一元运算符的前/后要不要加空格

"spaced-comment": 0,//注释风格要不要有空格什么的

"strict": 2,//使用严格模式

"use-isnan": 2,//禁止比较时使用NaN,只能用isNaN()

"valid-jsdoc": 0,//jsdoc规则

"valid-typeof": 2,//必须使用合法的typeof的值

"vars-on-top": 2,//var必须放在作用域顶部

"wrap-iife": [2, "inside"],//立即执行函数表达式的小括号风格

"wrap-regex": 0,//正则表达式字面量用小括号包起来

"yoda": [2, "never"]//禁止尤达条件

 

ReactNative 技术

一  前言

FaceBook早期开源发布了React Native For IOS,终于在2015年9月15日也发布了React Native for Android,虽然Android版本的项目发布比较迟,但是也没有阻挡了广大开发者的热情。可以这样讲在2015年移动平台市场上有两个方向技术研究比较火,第一种为阿里,百度,腾讯,携程,360等一线互联网公司的插件化,热修改等技术,第二种就是广大开发者一直在讨论的React Native技术。前几天同我在美国的童鞋了解到,在国外现在很多创业型互联网公司都在使用React Native技术,由此可以看出该项技术的前景还是不错的。我在这边我自己就大胆预测一下吧(呵呵,勿喷哦~):2016年将是React Native大力发展的一年。所以对于我自己来而言,2016年的主要技术方向,React Native是占了主角了。在接下来的很长一段时间内,我的博客会持续更新React Native的应用开发教程,欢迎大家关注以及相关交流!

二 React Native介绍

React Native 是伟大的互联网公司Facebook与2015年9月15日发布的,该可以让我们广大开发者使用JavaScript和React开发我们的应用,该提倡组件化开发,也就是说React Native给我们提供一个个封装好的组件让开发者来进行使用,甚至我们可以相关嵌套形成新的组件。使用React Native我们可以维护多种平台(Web,Android和IOS)的同一份业务逻辑核心代码来创建原生应用。现阶段Web APP的的体验还是无法达到Native APP的体验,所以这边fackbook更加强调的是learn once,write everywhere,应用前端我们使用js和React来开发不同平台的UI,下层核心模块编写复用的业务逻辑代码,提供应用开发效率。