最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

淺談開(kāi)發(fā)eslint規(guī)則

 更新時(shí)間:2018年10月01日 11:53:53   作者:orangexc  
這篇文章主要介紹了淺談開(kāi)發(fā)eslint規(guī)則,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

開(kāi)發(fā) eslint 規(guī)則

前端的日常開(kāi)發(fā)離不開(kāi)各種 lint 的支持,使用 lint 的一種誤解是:個(gè)人能力不足,必須 lint 規(guī)范才能寫出規(guī)范的代碼,實(shí)際上規(guī)范的定義主要取決于開(kāi)源項(xiàng)目作者的習(xí)慣,或者公司團(tuán)隊(duì)編碼的習(xí)慣,即使兩個(gè)前端專家,寫出的代碼規(guī)范也會(huì)有差別。

今天主題聊聊 eslint,作為最主流的 JavaScript lint 工具深受大家喜愛(ài),而 JSHint 卻逐漸淡出了大家的視線,使用的比較少了

常用的 eslint 擴(kuò)展有 standard,airbnb 等

剖析 eslint 擴(kuò)展

擴(kuò)展無(wú)非就作兩個(gè)事情

  • 在原有的 eslint 的基礎(chǔ)上配置些 config(具體規(guī)則參數(shù),全局變量,運(yùn)行環(huán)境等)
  • 自定義些自己的 rule,以滿足需求

原理就是利用 eslint 的繼承模式,理論上可以無(wú)限繼承并覆蓋上一級(jí)的規(guī)則

第一條不詳細(xì)介紹了,eslint 官網(wǎng)說(shuō)的十分詳細(xì),基本每一條規(guī)則都支持自定義參數(shù),覆蓋面也特別廣,基本上所有的語(yǔ)法都有 rule

第二條的自定義 rule 才是本文的重頭戲,因?yàn)樘厥獾臉I(yè)務(wù)場(chǎng)景靠 eslint 自身配置已經(jīng)無(wú)法滿足業(yè)務(wù)需求了,如:

  • eslint-plugin-vue
  • eslint-plugin-react
  • eslint-plugin-jest

一般特殊場(chǎng)景的自定義規(guī)則都使用 eslint-plugin-* 的命名,使用時(shí)可以方便的寫成

{
 plugins: [
 'vue',
 'react',
 'jest'
 ]
}

當(dāng)然 eslint-config-* 同理,不過(guò)配置時(shí)需要寫成

{
 extends: 'standard'
}

下面介紹下開(kāi)發(fā)流程

創(chuàng)建 eslint plugin 工程

官方推薦使用 yeoman 生成項(xiàng)目,感覺(jué)生成的項(xiàng)目比較守舊,推薦下習(xí)慣我的項(xiàng)目結(jié)構(gòu)

eslint-plugin-skr
 |- __tests__
 | |- rules
 | |- utils
 |
 |- lib
 | |- rules
 | |- utils
 | |- index.js
 |
 |- jest.config.js
 |
 |- package.json
 |
 |- README.md

整體看下來(lái)發(fā)現(xiàn)多了 jest 配置文件,是的 yeoman 生成的項(xiàng)目默認(rèn)采用 Mocha 作為測(cè)試框架,個(gè)人感覺(jué)調(diào)試起來(lái)麻煩,沒(méi)有 jest 靈活,vscode 輕松搞定調(diào)試

教程一搜一大把哈,給伸手黨一個(gè)鏈接debugging-jest-tests

關(guān)于 jest 的 config 文件也po出來(lái)一下,都是些基本的配置,復(fù)雜的用不到,下面會(huì)詳細(xì)介紹測(cè)試部分

module.exports = {
 testEnvironment: 'node',
 roots: ['__tests__'],
 resetModules: true,
 clearMocks: true,
 verbose: true
}

自定義的規(guī)則全部在 lib/rules 下面,每條規(guī)則單獨(dú)一個(gè)文件足以

下面一個(gè)簡(jiǎn)單的例子打通任督二脈

開(kāi)發(fā)一個(gè)規(guī)則

前期準(zhǔn)備

這個(gè)官方文檔寫的密密麻麻,好幾十個(gè)屬性,其實(shí)只是冰山一角,有很多復(fù)雜場(chǎng)景需要考慮

有人疑問(wèn):一定需要精通 AST?

我的回答是當(dāng)然不需要,簡(jiǎn)單了解便是,最起碼知道解析出來(lái)的語(yǔ)法樹(shù)大體結(jié)構(gòu)長(zhǎng)什么樣子

那就隨便給自己一個(gè)命題寫吧!寫個(gè)超級(jí)簡(jiǎn)單的

module.exports = {
 meta: {
 docs: {
  description: '禁止塊級(jí)注釋',
  category: 'Stylistic Issues',
  recommended: true
 }
 },

 create (context) {
 const sourceCode = context.getSourceCode()

 return {
  Program () {
  const comments = sourceCode.getAllComments()

  const blockComments = comments.filter(({ type }) => type === 'Block')

  blockComments.length && context.report({
   message: 'No block comments'
  })
  }
 }
 }
}

具體寫法官方文檔有介紹哈,就不贅述了,例子也十分簡(jiǎn)單,調(diào)用了環(huán)境變量 context 中的方法獲取全部注釋

稍微復(fù)雜點(diǎn)的場(chǎng)景

如需要 lint bar 對(duì)象中屬性的順序,如下假設(shè)一個(gè)規(guī)則

// good
const bar = {
 meta: {},
 double: num => num * 2
}

// bed
const bar = {
 double: num => num * 2,
 meta: {},
}

這個(gè)第一次些會(huì)有些蒙,官網(wǎng)沒(méi)有提供具體的例子,解決辦法很簡(jiǎn)單,推薦一個(gè)利器astexplorer

點(diǎn)進(jìn)去別著急復(fù)制代碼查看 AST 結(jié)果,首先選擇 espree(eslint 使用的語(yǔ)法解析庫(kù)),如下

這短短的四行代碼會(huì)對(duì)應(yīng)著一個(gè)抽象語(yǔ)法樹(shù),如下圖:

由于全展開(kāi)太長(zhǎng)了哈,感興趣的自行嘗試,會(huì)發(fā)現(xiàn)層級(jí)嵌套的特別深,找到 bar 的屬性需要 Program.body[0].declarations[0].init.properties

當(dāng)然不至于每次都從最頂級(jí)的 Program 找下來(lái),從上面的例子可以看出 create 方法的 return 返回的是一個(gè) object,里面可以定義很多檢測(cè)類型,如官網(wǎng)的例子:

function checkLastSegment (node) {
 // report problem for function if last code path segment is reachable
}

module.exports = {
 meta: { ... },
 create: function(context) {
 // declare the state of the rule
 return {
  ReturnStatement: function(node) {
  // at a ReturnStatement node while going down
  },
  // at a function expression node while going up:
  "FunctionExpression:exit": checkLastSegment,
  "ArrowFunctionExpression:exit": checkLastSegment,
  onCodePathStart: function (codePath, node) {
  // at the start of analyzing a code path
  },
  onCodePathEnd: function(codePath, node) {
  // at the end of analyzing a code path
  }
 }
 }
}

這里可以使用 VariableDeclarator 類型作為檢察目標(biāo),從下面的解析樹(shù)可以分析出篩選條件

VariableDeclarator 對(duì)象作為當(dāng)前的 node

當(dāng)變量名為 bar ,即 node.id.name === 'bar' ,且值為對(duì)象,即 node.init.type === 'ObjectExpression' ,代碼如下:

module.exports = {
 meta: { ... },
 create (context) {
 return {
  VariableDeclarator (node) {
  const isBarObj = node.id.name === 'bar' &&
   node.init.type === 'ObjectExpression'

  if (!isBarObj) return

  // checker
  }
 }
 }
}

就這樣成功取到 bar 對(duì)象后就可以檢測(cè)屬性的順序了,排序算法一大把,挑一個(gè)喜歡的用就行了,這里不啰嗦了,直接上結(jié)果:

const ORDER = ['meta', 'double']

function getOrderMap () {
 const orderMap = new Map()

 ORDER.forEach((name, i) => {
 orderMap.set(name, i)
 })

 return orderMap
}

module.exports = {
 create (context) {
 const orderMap = getOrderMap()

 function checkOrder (propertiesNodes) {
  const properties = propertiesNodes
  .filter(property => property.type === 'Property')
  .map(property => property.key)

  properties.forEach((property, i) => {
  const propertiesAbove = properties.slice(0, i)
  const unorderedProperties = propertiesAbove
   .filter(p => orderMap.get(p.name) > orderMap.get(property.name))
   .sort((p1, p2) => orderMap.get(p1.name) > orderMap.get(p2.name))

  const firstUnorderedProperty = unorderedProperties[0]

  if (firstUnorderedProperty) {
   const line = firstUnorderedProperty.loc.start.line

   context.report({
   node: property,
   message: `The "{{name}}" property should be above the "{{firstUnorderedPropertyName}}" property on line {{line}}.`,
   data: {
    name: property.name,
    firstUnorderedPropertyName: firstUnorderedProperty.name,
    line
   }
   })
  }
  })
 }

 return {
  VariableDeclarator (node) {
  const isBarObj = node.id.name === 'bar' &&
   node.init.type === 'ObjectExpression'

  if (!isBarObj) return

  checkOrder(node.init.properties)
  }
 }
 }
}

這里代碼有點(diǎn)多,耐心看完其實(shí)挺簡(jiǎn)單的,大致解釋下

getOrderMap 方法將數(shù)組轉(zhuǎn)成 Map 類型,方面通過(guò) get 獲取下標(biāo),這里也可以處理多緯數(shù)組,例如兩個(gè) key 希望在相同的排序等級(jí),不分上下,可以寫成:

const order = [
 'meta'
 ['double', 'treble']
]

function getOrderMap () {
 const orderMap = new Map()

 ORDER.forEach((name, i) => {
 if (Array.isArray(property)) {
  property.forEach(p => orderMap.set(p, i))
 } else {
  orderMap.set(property, i)
 }
 })

 return orderMap
}

這樣 doubletreble 就擁有相同的等級(jí)了,方便后面擴(kuò)展,當(dāng)然實(shí)際情況會(huì)有 n 個(gè)屬性的排序規(guī)則,也可以在這個(gè)規(guī)則上輕松擴(kuò)展,內(nèi)部的 sort 邏輯就不贅述了。

開(kāi)發(fā)就介紹到這里,通過(guò)上面安利的在線語(yǔ)法解析工具可以輕松反推出 lint 邏輯。

如果 rule 比較復(fù)雜,就需要大量的 utils 支持,不然每個(gè) rule 都會(huì)顯得一團(tuán)糟,比較考驗(yàn)公共代碼提取的能力

測(cè)試

如前面所講建議使用 jest 進(jìn)行測(cè)試,這里的測(cè)試和普通的單元測(cè)試還不太一樣,eslint 是基于結(jié)果的測(cè)試,什么意思呢?

lint 只有兩種情況,通過(guò)與不通過(guò),只需要把通過(guò)和不通過(guò)的情況整理成兩個(gè)數(shù)組,剩下的工作交給 eslint 的 RuleTester 處理就行了

上面的屬性排序 rule,測(cè)試如下:

const RuleTester = require('eslint').RuleTester
const rule = require('../../lib/rules/test')

const ruleTester = new RuleTester({
 parserOptions: {
 ecmaVersion: 6
 }
})

ruleTester.run('test rule', rule, {
 valid: [
 `const bar = {
  meta: {},
  double: num => num * 2
 }`
 ],
 invalid: [
 {
  code: `const bar = {
  double: num => num * 2,
  meta: {},
  }`,
  errors: [{
  message: 'The "meta" property should be above the "double" property on line 2.'
  }]
 }
 ]
})

valid 中是希望通過(guò)的代碼, invalid 中是不希望通過(guò)的代碼和錯(cuò)誤信息,到這里一個(gè) rule 算是真正完成了。

打包輸出

最后寫好的 rules 需要發(fā)一個(gè) npm 包,以便于在項(xiàng)目中使用,這里就不贅述怎么發(fā)包了,簡(jiǎn)單聊聊怎么優(yōu)雅的把 rules 導(dǎo)出來(lái)。

直接上代碼:

const requireIndex = require('requireindex')

// import all rules in lib/rules
module.exports.rules = requireIndex(`${__dirname}/rules`)

這里使用了三方依賴 requireindex ,對(duì)于批量的導(dǎo)出一個(gè)文件夾內(nèi)的所有文件顯得簡(jiǎn)潔很多。

當(dāng)然前提是保證 rules 文件夾下都是 rule 文件,不要把 utils 寫進(jìn)去哈

總結(jié)

行文目的是國(guó)內(nèi)外對(duì)于自定義 eslint rule 的相關(guān)資源較少,希望分享一些寫自定義規(guī)則的經(jīng)驗(yàn)。

千萬(wàn)不要在學(xué)習(xí) AST 上浪費(fèi)時(shí)間,不同的庫(kù)對(duì) AST 的實(shí)現(xiàn)是不同的,下次寫 babel 插件又要學(xué)其它的 AST 規(guī)則,再次安利一下 AST 神器astexplorer,只要把需要驗(yàn)證的代碼放到 astexplorer 里跑一遍,然后總結(jié)出規(guī)律,邏輯其實(shí)十分簡(jiǎn)單,對(duì) AST 結(jié)果進(jìn)行判斷就行了。

從團(tuán)隊(duì)層面講,希望所有的團(tuán)隊(duì)都有自己的 eslint 規(guī)則庫(kù),可以大大降低 code review 的成本,又能保證代碼的一致性,一勞永逸的事情。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論

宝鸡市| 西乌珠穆沁旗| 乾安县| 文山县| 湖州市| 玛沁县| 瑞金市| 开封市| 鄄城县| 汝南县| 益阳市| 苍山县| 南昌市| 潞城市| 紫云| 女性| 申扎县| 定边县| 镇雄县| 隆昌县| 开化县| 文安县| 盐源县| 桑植县| 新巴尔虎右旗| 衢州市| 曲周县| 临高县| 宝应县| 元朗区| 化州市| 突泉县| 苍梧县| 尚义县| 聊城市| 洪雅县| 福清市| 时尚| 阜宁县| 广宁县| 甘孜县|