1. 程式人生 > >一款檢測程式碼中TODO的eslint外掛

一款檢測程式碼中TODO的eslint外掛

# 一款檢測程式碼中TODO的eslint外掛 ## 前言 看了我標題進來的同學應該也知道我做的是個啥東西 沒錯是一個[eslint](https://eslint.org/)外掛,前端魔法師們日常所使用的工具之一 什麼?你不知道eslint是幹嘛的--吃鯨.jpg * ESLint 是一個開源的 JavaScript 程式碼檢查工具 * 能在多人協作專案中幫助統一程式碼風格 **百聞不如[一見](https://github.com/ATQQ/eslint-plugin-todo-ddl)**,先帶大家看看[外掛](https://github.com/ATQQ/eslint-plugin-todo-ddl)效果 ![圖片](https://img.cdn.sugarat.top/mdImg/MTYxNTgxMTk1MjM4MA==615811952380) 因為在工作中臨時會插入許多其它的事,或者有些程式碼,介面是有時效性的需要手動下線 外掛功能簡單講就是為不完善的程式碼**做標記**,提醒自己如期修改完善bug 想親自嘗試一下的同學戳[這裡](https://github.com/ATQQ/eslint-plugin-todo-ddl)---順手Star 下面開始介紹為什麼會寫這個外掛,以及過程中遇到的問題,和外掛的一些核心方法 ## 背景 前端魔法師們,經常不經意間就會在程式碼中下毒,坑害同事,甚至直接使"線上爆炸" 當時遇到一個線上問題就是如此: * 一個有實效性的活動入口,需要到點下線,一個需要常駐的活動入口,不需要下線 * 由於實效性活動下線時間還有大半年 coder為了圖方便,直接把實效性活動業務元件拿著複用,想著到時候改,反正時間還長 半年後的一天,客述就來了,說xx入口怎麼不見了,彼時開發此頁面的同事已經調走,目前的專案組基本都是後來的 第一反應是不是昨晚提交了什麼程式碼,把入口**嚇掉了**,然後就開始翻釋出記錄,發現並沒有發版,就奇怪了,好好地怎麼就不見了 然後同事們就開始排查程式碼了,不一會兒就發現了問題所在,然後快速的改掉上線,避免影響擴大 ## 事後覆盤 * Leader: 咱能不能用啥工具避免此類問題發生 * 菜雞: 阿巴阿巴一堆話 * Leader: 那行你說了這麼多,這個任務就交給你了,下週給個方案 * 菜雞: ...哦豁,要被捲鋪蓋走人了 ## 個人思考 在不完善的程式碼或有時效性的程式碼處搞一個flag,通過工具檢測這個flag然後提醒此專案的所有開發者 開發者開啟這個專案就能知道xx flag處有隱患 ## 調研-可行方案 ### cli工具 專案啟動後,跑一個npm script 掃描目標檔案,然後通過正則匹配flag關鍵字基於提示 ### vs code外掛 前端魔法師基本都用的Vs Code 專案啟動後,自動掃描目標檔案 通過正則匹配flag,然後基於提示 這個有現成的[todo-tree](https://github.com/Gruntfuggly/todo-tree) ### eslint規則 前端專案都會引入eslint 來統一團隊成員的編碼風格 只要編寫一個自定義的規則,通過分析eslint 提供的AST,就能輕易的拿到標記的flag ### 對比 | 方案 | 優點 | 缺點 | 學習成本 | | :---------: | :-----------------------------------------------------: | :-------------------------------------------------: | :------: | | cli工具 | 不限制語言/檔案型別,通過正則就能進行輕易的匹配 | 需要為每個專案單獨編寫一個指令,通過hook或者人工觸發 | ❤❤❤ | | vs code外掛 | 只需要使用者為編輯器安裝一個外掛,即可在開啟的時候進行提示 | 魔法師們可能用sublime/webStorm ,需要重複開發外掛 | ❤❤❤❤ | | eslint規則 | 只需要在專案的配置檔案中加入一行規則程式碼,即可實時的提示 | 需要安裝依賴,首次需要手動引入 | ❤❤ | ### 最終選擇 基於eslint編寫一個eslint-plugin來實現自定義校驗規則 **原因** * 前端專案都會引入eslint * 不限制編輯器 * 理論上不限制前端開發常用的語言/框架的檢測 * 標準化團隊統一了所有倉庫的lint規則,只需在標準化規則中加入即可 * 其餘倉庫,只要執行依賴安裝(yarn/npm i) 就會將最新的規則引入專案,使用者能0配置接入 ## 實現過程 下面會展開介紹外掛的實現原理,會涉及到一些程式碼的展示 首先eslint會將響應的檔案用AST描述出來,並且提供了一些簡單的API進行操作 ```js create(context) { // 取得AST const sourceCode = context.getSourceCode() // 獲取所有的註釋節點 let comments = sourceCode.getAllComments() } ``` 註釋節點有兩種 ```js // 註釋 /** * Block **/ ``` 從註釋節點中過濾出以flag關鍵字開始的註釋節點 ```js // 過濾出包含關鍵詞的註釋節點 comments = comments.filter(comment => { let { value, type } = comment // 展平塊狀註釋 if (type === 'Block') { value = value.replace(/\*|\n/g, '') } value = value.toLowerCase().trim() // 儲存格式化後的字串 comment.newValue = value for (const flag of dFlag) { // 檢測是否一關鍵字開頭 if (value.startsWith(flag)) { // 儲存上flag comment.flag = flag return true } } return false }) ``` 此時過濾後的註釋基本格式如下 ```js // flag ddl:time xxxxx ``` 接下來從中解析出**ddl**和提示資訊 首先ddl也是一個可配置的關鍵字 需要檢測的日期格式如下 | format1 | demo | format2 | demo | format3 | demo | | :--------: | :--------: | :--------: | :--------: | :------: | :------: | | yyyy-mm-dd | 2020-06-01 | yyyy/mm/dd | 2020/06/01 | yyyymmdd | 20200601 | | | 2020-06-1 | | 2020/06/1 | | 200601 | | | 2020-6-01 | | 2020/6/01 | | | 20-06-01 | | 20/06/01 | | | 20-6-1 | | 20/6/1 | | | 20-6-01 | | 20/6/01 | 所以程式碼中做了許多判斷 * 程式碼可能有點shi,看官們可以給點建議優化下 ```js // 匹配日期的正則 const rDate = [{ reg: /((\d{4})|(\d{2}))(-((0\d)|(\d{2})|(\d{1}))){2}/, flag: '-' // yyyy-mm-dd|yy-mm-dd }, { reg: /((\d{4})|(\d{2}))(\/((0\d)|(\d{2})|(\d{1}))){2}/, flag: '/'// yyyy/mm/dd|yy/mm/dd }, { reg: /(\d{8})|(\d{6})/, flag: 'number'// yyyymmdd|yymmdd }] /** * 獲取TODO註釋中的DDL,是則返回日期值及其todo內容 * @param {String} value 待操作字串 * @param {String[]} ddlSymbol 截止時間識別符號 * @param {STring} todoSymbol * @return {Object} */ function getDDLAndText(value, ddlSymbol, todoSymbol) { let text = value.slice(value.indexOf(ddlSymbol) + ddlSymbol.length), date = '' for (const rdate of rDate) { const { reg, flag } = rdate const res = text.match(reg) if (res) { const [dateStr] = res // 再次校驗匹配的日期日期是否合法 if (reg.test(dateStr)) { let year, month, day if (flag !== 'number') { let ymd = dateStr.split(flag) ymd = ymd.map(v => { return v.length === 1 ? `0${v}` : v }) year = ymd[0] month = ymd[1] day = ymd[2] } else { const { length } = dateStr day = dateStr.slice(length - 2) month = dateStr.slice(length - 4, length - 2) year = dateStr.slice(0, length - 4) } if (year.length === 2) { year = new Date().getFullYear().toString().slice(0, 2) + year } text = text.slice(text.indexOf(dateStr) + dateStr.length) date = `${year}-${month}-${day}` // 日期不合格也pass掉 if (month > 12 || day > 31) { date = '' } break } } } return { text, date } } ``` 這樣就拿到了flag的**截止日期**與**內容** 接下來只需要根據設定的時間警戒線進行提示即可 ```js // 未設定DDL或者DDL不合法情況 if (!date) { errMsg = '沒有設定有效的Deadline,設定方法(https://github.com/ATQQ/eslint-plugin-todo-ddl)' } else { const TODODate = new Date(date).getTime() const interval = TODODate - Date.now() // 如果已經到期 if (interval < 0 || interval < oneDay) { errMsg = '已經過截止日期,請立即修改' } else { // 剩餘天數(向下取整) const theRestDays = ~~(interval / oneDay) errMsg = theRestDays <= dWarnLine ? `還有${theRestDays}天截止,請儘快修改` : '' } } if (errMsg) { context.report({ node: comment, message: `TODO WARN: ${errMsg} --> ${text}` }) } ``` 到此就大工搞成了,可以發包上線了 ## 總結思考 ### 不足之處 1. 程式碼還有可改進之處 2. 外掛上線後並沒大改過,功能感覺還可以增強,需要朋友們多給點意見 ### 收穫 1. leader還是評價不錯,畢竟那時候實習生身份剛入職幾天 2. 自己也有所提升,對eslint的工作原理有了新的認識 3. 也為團隊日後各種花式校驗規則提供了技術積累 ### 未來 1. 找時間根據反饋,迭代一下外掛,提高其可玩性 ## 其它 * [eslint外掛開發教程文件](../learn/eslint-plugin.md) * [vs code外掛 todo-tree](https://github.com/Gruntfuggly/todo-tree)也很棒喲 >[本文](https://juejin.cn/post/6939877553582637069)正在參與「掘金 2021 春招闖關活動」, 點選檢視 [活動詳情](https://juejin.cn/post/6939329638506