用 TS + Vue 寫了一個在 Chrome 中執行 Prettier 格式化的擴充套件程式

u3u發表於2018-05-29

用 TS + Vue 寫了一個在 Chrome 中執行 Prettier 格式化的擴充套件程式

在介紹 prettier-chrome 之前請允許我先介紹一下什麼是 Prettier
如果你已經知道這是什麼了並且已經會配置了,那麼你可以直接跳到 prettier-chrome 的介紹

什麼是 Prettier ?

  • 有見識的程式碼格式化工具
  • 支援多種語言
  • 與大多數編輯器整合
  • 零配置,開箱即用(但你也可以少量配置)

為什麼使用 Prettier ?

  • 儲存時自動格式化
  • 無需在程式碼審查中討論風格
  • 節省您的時間和精力
  • 和更多

支援的語言

JavaScript

  • ES2017
  • JSX
  • Flow
  • TypeScript
  • Vue
  • JSON

CSS

  • CSS3+
  • Less
  • SCSS
  • styled-components
  • styled-jsx

GraphQL

  • GraphQL
  • GraphQL Schemas

Markdown

  • CommonMark
  • GitHub Flavored Markdown

Work in Progress

  • Elm (via elm-format)
  • Java
  • PHP
  • PostgreSQL
  • Python
  • Ruby
  • Swift

支援的編輯器

  • Atom (prettier-atom / mprettier / miniprettier)
  • Emacs (prettier-emacs)
  • Espresso (espresso-prettier)
  • Sublime Text (JsPrettier)
  • Vim (neoformat / ale / vim-prettier)
  • Visual Studio (JavaScriptPrettier)
  • VS Code (prettier-vscode)
  • WebStorm (Built-in support)

可以看到 Prettier 是一個非常強大的格式化程式,目前唯一的缺陷是暫時還不支援格式化 HTML 檔案
因為 HTML 的情況比較複雜,它還需要考慮 Vue 和 Angular 的模版語法,不過這已經在計劃中了

大家可以在 Playgournd 中體驗 Prettier 的所有功能:

image

食用 Prettier 的正確姿勢

在這裡向大家介紹一下如何正確使用 Prettier 來統一程式碼風格

1. 如何結合 ESLint 使用?

如果你想與 ESLint 結合使用,那麼有兩個方案:

我希望格式化結果完全符合 Prettier 的要求

eslint-plugin-prettier 的工作原理是,對比格式化前和用 Prettier 格式化後的程式碼,有不一致的地方就會報錯,然後你可以手動執行 eslint --fix 來修復。

不過 Prettier 的格式化很可能和你使用的 ESLint 配置衝突,比如你的 ESLint 設定的不使用分號,而 Prettier 預設使用分號,這時候你需要配置 Prettier 讓它不使用分號。反之如果你想使用 Prettier 的規則而不是 ESLint 的,為防止 ESLint 報錯,你需要使用 eslint-config-prettier 來關閉所有可能引起衝突的規則。

{
  "scripts": {
    "lint": "eslint *.js"
  },
  "eslintConfig": {
    "extends": ["prettier"],
    "plugins": ["prettier"],
    "rules": {
      "prettier/prettier": "error"
    }
  },
  "prettier": {
    "singleQuote": true,
    "semi": false
  }
}
複製程式碼

你可以使用大多數編輯器裡 ESLint 外掛提供的 autoFixOnSave 選項來在儲存時自動格式化。

希望格式化結果完全符合 ESLint 的要求(推薦)

使用 ESLint 與 eslint-plugin-prettier 的結果是最終得到的程式碼是充分尊重 Prettier 的結果,而 prettier-eslint-cli 則是先執行 Prettier 然後再自動使用 eslint --fix 將與 ESLint 規則衝突的程式碼修正成 ESLint 想要的結果。這樣其實引入 Prettier 不會影響你原有的設定。

{
  "scripts": {
    // ESLint 只負責檢查程式碼質量
    "lint": "eslint *.js",
    // 用 Prettier 格式化程式碼並自動 `eslint --fix `以便下次執行 `npm run lint` 時不會報錯
    "format": "prettier-eslint --write *.js"
  },
  "eslintConfig": {
    "extends": "some-config"
  },
  "prettier": {
    "singleQuote": true,
    "semi": false
  }
}
複製程式碼

除了命令列的 npm run format,同時你也可以使用編輯器裡的 Prettier 外掛,並啟用 eslintIntegration 來在編輯器裡自動格式化程式碼。

這一段摘自 EGOIST 大佬的部落格《用 ESLint 和 Prettier 寫出高質量程式碼》
原文地址:https://egoist.moe/2017/12/11/write-better-code-with-eslint-and-prettier

這裡我想讓大家注意一點,就是 eslint 和 prettier 命令的匹配引數最好加上雙引號,否則可能出現錯誤。

2. 使用 gitHooks 在提交程式碼時自動格式化

Q: 為什麼要使用提交鉤子?我編輯器已經安裝了 prettier 外掛了,豈不是多此一舉?
A: 如果是你的個人專案那麼無所謂。如果是多人開發或者開源專案的話,你不能保證所有人都安裝編輯器外掛,所以需要使用 gitHooks 來保證風格統一。

安裝 yorkielint-staged 來配置提交鉤子

yarn add yorkie lint-staged --dev
複製程式碼

yorkie 是尤大 fork 自 husky 進行少量更改的提交鉤子,在 @vue/cli 3.x 中內建。

當然,別忘了安裝 prettier 模組

yarn add prettier --dev
複製程式碼

安裝好了之後在 package.json 中的 gitHookslint-staged 屬性中配置提交鉤子

{
  "gitHooks": {
    "pre-commit": "lint-staged"
  },
  "lint-staged": {
    "*.{ts,tsx,js,jsx,json,scss,md}": ["prettier --write", "git add"]
  }
}
複製程式碼

正常情況下安裝 yorkie 後會自動安裝提交鉤子
如果提交鉤子未生效可以手動執行 node node_modules/yorkie/bin/install.js 來安裝。
當然,你也可以執行 node node_modules/yorkie/bin/uninstall.js 來解除安裝提交鉤子。

3. 在 CI 上檢查程式碼是否忘記格式化

Q: 我已經配置了 gitHooks 了,為什麼還要在 CI 上檢查?豈不是多此一舉?
A: 是的,gitHooks 可以保證大部分情況。但是別忘了,如果別人直接在 Github 上提交程式碼的話是沒有提交鉤子的哦。

安裝 prettier 和 prettier-check 來檢查是否忘記格式化程式碼

yarn add prettier prettier-check --dev
複製程式碼

安裝好了之後在 package.json 中的 scripts 屬性中配置如下:

{
  "scripts": {
    "lint:prettier": "prettier-check \"**/*.{ts,tsx,js,jsx,json,scss,md}\"",
    "format": "prettier \"**/*.{ts,tsx,js,jsx,json,scss,md}\" --write"
  }
}
複製程式碼

接下來你就可以使用 yarn lint:prettier 來檢查是否忘記格式化程式碼了
同時你可以使用 yarn format 來格式化所有匹配的檔案

接下來配置 CI,以 Travis CI 為例子,配置如下:

language: node_js

node_js: 9

install:
  - yarn

script:
  - yarn lint
  - yarn lint:prettier

cache:
  yarn: true
  directories:
    - node_modules
複製程式碼

這樣當你提交程式碼後 Travis 就會執行檢查命令,以保證程式碼風格統一。
同時如果有人給你的專案 Pull request,也會進行檢查,檢查失敗可以要求對方更改後再提交。

不過 prettier-check 只適用於“希望格式化結果完全符合 Prettier 的要求”的場景。所以我 fork 了一份 prettier-check 修改成了 prettier-eslint-check 可以用來檢查“希望格式化結果完全符合 ESLint 的要求”的場景,只需要把 prettier-check 改成 prettier-eslint-check 即可。

yarn add prettier prettier-eslint-{cli,check} --dev
複製程式碼
{
  "scripts": {
    "lint:prettier":
      "prettier-eslint-check \"**/*.{ts,tsx,js,jsx,json,scss,md}\"",
    "format": "prettier-eslint \"**/*.{ts,tsx,js,jsx,json,scss,md}\" --write"
  }
}
複製程式碼

prettier-check 和 prettier-eslint-check 的使用方式和 prettier 一致,只是去掉了 --write 引數

prettier-chrome

好了,Prettier 的介紹和使用方式都已經說了,接下來我要來介紹一下我寫的 prettier-chrome 擴充套件了!
先給大家放一張動態圖,相信看完之後大家就知道是幹嘛用的了!

用 TS + Vue 寫了一個在 Chrome 中執行 Prettier 格式化的擴充套件程式

沒錯!這是一款可以讓 Prettier 執行在 Chrome 的擴充套件程式!
它主要用來線上寫 markdown 時格式化,適用於:

  • sf.gg 答題
  • gayhub 提 issues、pr
  • 線上寫部落格
  • 等等...

主要功能:

  • 中英文之間自動新增空格
  • 自動對齊表格
  • 自動對齊列表
  • 程式碼塊指定的語言如果支援的話會使用對應的 parser 格式化
  • 等等...

用法非常簡單,你只需要在 markdown 編輯器獲得焦點的時候按下 ALT + F 快捷鍵即可格式化!

可以配置 Prettier 中的所有選項

用 TS + Vue 寫了一個在 Chrome 中執行 Prettier 格式化的擴充套件程式

這個擴充套件是基於昨天釋出的 Prettier v1.13.0 版本進行開發的,這個專案我已經蓄謀已久了,無奈之前版本的 Prettier 並不支援在瀏覽器中執行,所以我一直在關注 Prettier 的動態,終於在昨天 v1.13.0 釋出,並支援在瀏覽器中執行!於是老夫我擼起袖子就是一頓幹,然後申請把專案新增到 Prettier 官網中,並得到了官方人員的認可!

image

官方還轉推了 (๑>◡<๑)

image

我絕對沒有膨脹,我沒有!(留下了沒有技術的淚水,逃
順便問一句,大家有沒有覺得我這篇文章的排版特別舒服呀?這都是 Prettier 的功勞喲(

擴充套件的原理非常簡單,popup 頁面用來修改 Prettier 的各種選項並儲存到 localStorage 中。
在背景頁面(事件頁面)註冊右鍵選單和快捷鍵命令,通過 chrome.tabs.sendMessage 與內容指令碼通訊。
在內容指令碼通過 chrome.runtime.onMessage 監聽訊息,如果收到了格式化指令則獲取當前獲得焦點的元素判斷是否是一個多行文字框,如果是並且內容不為空則呼叫 prettier.format 方法將當前元素的內容和外掛還有 localStorage 中儲存的選項傳遞給它,獲得格式化的結果後重新賦值文字框。

原始碼在這裡:https://github.com/u3u/prettier-chrome
官方人員已 star,請各位放心食用(
歡迎各種 pr 和 review,喜歡的話別忘了點個 star 喲 (* >ω<)

專案架構

當然是選擇原諒色的 Vue 啦!
使用 @vue/cli 3.0.0-beta.12 生成一個 TS + Vue 全家桶的專案

  • 使用 ESLint 代替 TSLint
  • 使用渲染函式 JSX 代替模版
  • 使用 vue-emotion CSS in JavaScript 方案

使用 ESLint 代替 TSLint 的理由

ESLint 規則比 TSLint 多,並且有主流的規則可以選擇

使用 JSX 代替模版的理由

  • Prettier 暫不支援格式化 HTML
  • 使用渲染函式讓你對 Vue 的理解更深
  • JSX 比模版更靈活
  • JSX 的開發體驗比模版好(雖然現在 VSC 有 Vetur 擴充套件支援,但依然無法達到 JSX 的程度)
  • JSX 不限制你的標籤名(媽媽再也不用擔心我起的名和 HTML 內建標籤名重複的問題啦)

當然了,JSX 對初學者不友好。我初學 Vue 的時候都是跳過渲染函式這一節看的 ?

使用 vue-emotion CSS in JavaScript 方案的理由

因為已經使用 JSX 代替模版了,當然希望和單檔案元件一樣把 CSS 也寫在一起啦!
不過我研究了很多 CSS in JavaScript 的庫,基本都不支援 PostCSS 外掛。除了 styled-jsx,不過它只支援 React。所以正規專案最好不要使用,可以使用 CSS Modules 的方案代替(但這就需要將 CSS 檔案從 JS 中分離出來了)。

使用 Vue 只是為了方便寫擴充套件的 popup 頁面或者 option 頁面
使用 Vue-Router 來切換頁面,無需多入口打包。

{
  "browser_action": {
    "default_popup": "index.html#/popup"
  },
  "options_page": "index.html#/options"
}
複製程式碼

而背景頁面(事件頁面)和內容指令碼都是無頁面的,只需要指令碼,所以我使用 bili 來打包。

bili

bili 是 egoist 大佬基於 rollup 封裝的一個零配置打包工具。
egoist 大佬是 vue-cli 的核心開發者之一,大家可以放心使用。
順便說一句,3.x 的 vue-cli 借鑑了很多 egoist 大佬的想法,詳情:https://github.com/vuejs/vue-cli/issues/589

參考文件:https://bili.egoist.moe/#/api

CSP(內容安全策略)

在 Chrome 擴充套件中使用 Vue 只有一個問題,那就是 CSP 的問題。這一點在 Vue 的文件中也有說明。
在 Chrome 擴充套件環境中預設策略限制如下:

  • eval 及相關函式已禁用(eval / setTimeout / setInterval / new Function
  • 內嵌 JavaScript 程式碼將不會執行
  • 只有本地指令碼和物件資源才會載入

在我這個擴充套件中我使用放寬策略來讓 Vue 正常工作,在 manifest.json 中配置如下:

{
  "content_security_policy":
    "script-src 'self' 'unsafe-eval'; object-src 'self'"
}
複製程式碼

但是這樣配置之後還有一個問題,因為 3.x 的 vue-cli 生成環境下打包出來的指令碼是內嵌的
並且這個沒有放寬策略,於是我在 Issues 裡搜尋到了一個相同的問題:https://github.com/vuejs/vue-cli/issues/1074

通過配置 vue.config.js 刪除 split-manifestinline-manifest 即可。

module.exports = {
  chainWebpack: (config) => {
    config.plugins.delete('split-manifest').delete('inline-manifest');
  },
};
複製程式碼

參考文件:https://crxdoc-zh.appspot.com/extensions/contentSecurityPolicy

如何開發一個 Chrome 擴充套件?

概述:https://crxdoc-zh.appspot.com/extensions/overview
清單檔案格式:https://crxdoc-zh.appspot.com/extensions/manifest
JavaScript API:https://crxdoc-zh.appspot.com/extensions/api_index

相關文章