手摸手學(xué)會搭建一個 TS+Rollup 的初始開發(fā)環(huán)境
目前市面上有許多 CLI 工具用于快速初始化構(gòu)建一個項目。例如:tsdx、create-react-app,對于一些個人小項目來說實在是顯得又有些臃腫,如果拋開這些 CLI 工具,自己手動從 0 到 1 手動搭建一個小而美的 NPM 包開發(fā)環(huán)境需要做哪些工作?不妨一起動手試一下!
首先是我們的項目預(yù)期:
- 實現(xiàn)一個 Javascript 工具函數(shù)庫
- 支持 Typescript
- 團隊協(xié)作 commit message 格式約束
- Prettier 代碼格式化,ESlint 校驗
- 發(fā)包前自動升級版本并構(gòu)建
打包構(gòu)建工具采用 Rollup[1]。
一、構(gòu)建基本文件結(jié)構(gòu)
首先就是需要設(shè)計并搭建項目的文件結(jié)構(gòu),并初始化一個 package.json 文件,用于描述當(dāng)前項目的功能。
- mkdir dyboy-npm-ts-template
- cd dyboy-npm-ts-template
- touch index.ts
- npm init -y
二、初始化 Typescript 環(huán)境
可以看到我們的入口文件 ./index.ts 是 Typescript 類型,最重要的是我們需要打包給用戶,以支持在 TS 環(huán)境下的代碼提示,有助于提升研發(fā)效率
- yarn add typescript -D
然后初始化快速生成一個 tsconfig.json 文件,該文件屬于 Typescript 的配置文件
- tsc --init
默認(rèn)的 tsconfig.json 配置需要修改,以支持我們能夠編譯成 ES 模塊。
簡單修改后,符合需求的 tsconfig.json 配置以及目錄結(jié)構(gòu)如下:
這樣配置好后,Typescript 在編譯過程中會向 ./dist 目錄輸出 index.d.ts 的類型聲明文件
同時需要將 package.json 文件中修改/新增;
- - "main": "index.js",
- + "main": "./dist/index.cjs.js",
- + "module": "./dist/index.esm.js",
- + "types": "./dist/index.d.ts",
- + "files": [
- + "dist"
- + ],
files 字段是用于約定在發(fā)包的時候NPM 會發(fā)布包含的文件和文件夾。
注意: files 字段中文件夾名直接寫名字,不要包含 ./ 字符,否則打包出來的產(chǎn)物不會包含該文件夾。
到這一步,就可以流暢編寫 Typescript 文件
index.ts 文件內(nèi)容:
- const func = () => {
- // TODO: coding...
- };
- export { func };
鼠標(biāo)放函數(shù) func 上面也有了類型提示,表示 Typescript 開發(fā)環(huán)境已經(jīng) OK
三、Git 初始化
代碼存在本地,但為了更好的代碼版本管理/備份,將使用 Git 工具并和遠(yuǎn)程倉庫關(guān)聯(lián)起來。
首先初始化項目的 git
- git init
新建一個 .gitignore 文件,用于忽略那些不需要存入 Git 版本的文件或文件夾,并補充寫入規(guī)則如下:
- node_modules/
- dist/
- .DS_Store
- .yarn-error.log
四、初始化 Rollup 打包環(huán)境
因為我們發(fā)布 NPM 包,所以需要一個小巧精致的構(gòu)建工具,自然是選擇 Rollup 來作為打包工具,Rollup 較好地支持 tree shaking,使得打包出來的包體積更小。
4.1 安裝依賴:
- yarn add rollup -D
- yarn add @rollup/plugin-typescript -D
- yarn add @rollup/plugin-node-resolve -D
- yarn add @rollup/plugin-commonjs -D
安裝了 rollup,以及支持 TS、處理路徑和 commonjs 的插件
4.2 配置 rollup.config.js
初始化的配置如下
- import resolve from '@rollup/plugin-node-resolve';
- import typescript from '@rollup/plugin-typescript';
- import commonjs from '@rollup/plugin-commonjs';
- export default [
- {
- input: './src/index.ts',
- output: {
- dir: 'dist',
- format: 'cjs',
- entryFileNames: '[name].cjs.js',
- },
- plugins: [resolve(), commonjs(), typescript()],
- }, {
- input: './src/index.ts',
- output: {
- dir: 'dist',
- format: 'esm',
- entryFileNames: '[name].esm.js',
- },
- plugins: [resolve(), commonjs(), typescript()],
- }
- ];
可以同時生成支持 CommonJS 和 ESModule 的文件,在前面 tsconfig.json 配置下還會生成 index.d.ts 文件用于指明類型聲明。
4.3 修改 scripts
來到 package.json 文件中,為了使用快捷指令,以及調(diào)用 rollup 作為開發(fā)的預(yù)覽功能,需要配置 scripts 字段如下:
- "scripts": {
- + "dev": "rollup -w -c",
- + "build": "rollup -c"
- - "test": "echo \"Error: run tests from root\" && exit 1"
- },
如此,我們便可以通過 yarn dev,就能在開發(fā)的時候?qū)崟r編譯。
如果需要打包,則先執(zhí)行 yarn build 命令即可完成打包。
五、ESlint 配置
如果是小團隊協(xié)作開發(fā),就會涉及到代碼規(guī)范問題,換個角度,如果是開源的產(chǎn)品,有人提 MR 的時候,我們會希望他的代碼風(fēng)格是比較符合我們一些預(yù)期的,因此將在項目中引入 ESlint。
先安裝 eslint 開發(fā)環(huán)境依賴
- yarn add eslint -D
然后可以使用剛安裝好的 eslint 初始化一個配置文件:
- ./node_modules/.bin/eslint --init
這樣在項目的根目錄就有一個 .eslintrc.json 配置文件,
然后可以根據(jù)自己的需要去修改 ESlint 的一些規(guī)則,筆者配置好的規(guī)則內(nèi)容如下:
- {
- "env": {
- "browser": true,
- "commonjs": true,
- "es2021": true
- },
- "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
- "parser": "@typescript-eslint/parser",
- "parserOptions": {
- "ecmaVersion": 8
- },
- "plugins": ["@typescript-eslint"],
- "rules": {
- "@typescript-eslint/no-unused-vars": [
- "error",
- {
- "varsIgnorePattern": "^_"
- }
- ],
- "no-unused-vars": "off",
- "no-console": "warn",
- "space-before-function-paren": "warn",
- "semi": "warn",
- "quotes": ["warn", "single"]
- }
- }
六、Prettier 代碼自動格式化
前端開發(fā)項目中會涉及到一些代碼格式問題,比如函數(shù)括號后空格,CSS 格式,因此可以借助 Prettier 三方工具來實現(xiàn)團隊代碼的自動統(tǒng)一。
安裝 prettier
- yarn add prettier -D
然后新建一個 .prettierrc.json 的配置文件,內(nèi)容如下:
- {
- "printWidth": 100, //單行長度
- "tabWidth": 2, //縮進長度
- "useTabs": false, //使用空格代替tab縮進
- "semi": true, //句末使用分號
- "singleQuote": true, //使用單引號
- "bracketSpacing": true, //在對象前后添加空格-eg: { foo: bar }
- "arrowParens": "avoid" //單參數(shù)箭頭函數(shù)參數(shù)周圍使用圓括號-eg: (x) => x
- }
更多使用規(guī)則可以參考:Configuration File[2]
開發(fā)者可以通過快捷鍵 Shift + Commond + F 來實現(xiàn)當(dāng)前文件的格式化排版。
七、Husky Git 提交約束
當(dāng)然除了手動格式化,如果開發(fā)者沒有格式化就提交代碼到遠(yuǎn)程了怎么辦,為此,引入 Husky 作為 Git commit 提交前做一個自動格式化暫存區(qū)內(nèi)的文件,以及校驗是否符合 Eslint 規(guī)則。
與此同時,還需要將用戶的 git commit message 規(guī)范,可以引入 commitlint 工具,用于校驗提交的 message 格式是否符合規(guī)范
具體的規(guī)范可以參考:Conventional Commits[3]
首先安裝開發(fā)環(huán)境依賴:
- yarn add husky@3.1.0 -D
- yarn add lint-staged -D
- yarn add @commitlint/cli -D
- yarn add @commitlint/config-conventional -D
然后在 package.json 文件中新增如下內(nèi)容:
- "husky": {
- "hooks": {
- "pre-commit": "lint-staged",
- "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
- }
- },
- "commitlint": {
- "extends": [
- "@commitlint/config-conventional"
- ]
- },
- "lint-staged": {
- "*.{ts,js}": [
- "node --max_old_space_size=8192 ./node_modules/.bin/prettier -w",
- "node --max_old_space_size=8192 ./node_modules/.bin/eslint --fix --color",
- "git add"
- ]
- },
這樣配置好了后,開發(fā)者在 git commit 時,會首先調(diào)用 lint-staged 字段中命令,首先是 prettier 格式化,然后是 ESlint 校驗并修復(fù),然后將修改后的文件存入暫存區(qū)。
然后是校驗 commit message 是否符合規(guī)范,符合規(guī)范后才會成功 commit。
八、預(yù)發(fā)包前升級版本并構(gòu)建
為了實現(xiàn)快捷發(fā)包,可以在 package.json 文件的 scripts 字段添加快捷命令,用于規(guī)范發(fā)包。
添加如下命令,可以在發(fā)包(執(zhí)行 npm publish 命令)之前首先會提示升級包本本,然后 build 構(gòu)建出產(chǎn)物
- "prepublish": "yarn version && yarn build"
九、完成初始化
由于是開源庫,所以咱把 package.json -> license 字段的值改為 MIT,可以參考:什么是MIT 協(xié)議[4] 。
初始化的收尾工作就是將當(dāng)前的變動存入 Git 記錄中并關(guān)聯(lián)遠(yuǎn)程倉庫。
Git 遠(yuǎn)程同名倉庫的創(chuàng)建就不贅述了,可以參考:創(chuàng)建倉庫 - Github Docs[5]
然后我們將本地的代碼 staged 并 commit
- git add -A
- git commit -m "feat: init"
關(guān)聯(lián)遠(yuǎn)程倉庫并將本地代碼提交到遠(yuǎn)程倉庫:
- git remote add origin "https://xxxx.com/xx/xx.git"
- git push -u origin master
此后,我們可以把當(dāng)前倉庫作為一個開發(fā)支持 Typescript 的 NPM 包的模板倉庫,后續(xù)開發(fā)新的 NPM 包,只需要克隆當(dāng)前模板,然后再根據(jù)需要修改配置,新增 rollup 編譯插件等就可以啦!
總結(jié)
梳理了在初始化構(gòu)建一個工程項目中需要做的事情,涉及打包構(gòu)建、開發(fā)、Git、發(fā)包的內(nèi)容,從 0 到 1 愉快地完成了項目的初始化,后續(xù)就可以更加愉快地開發(fā)了。
TODO: 可以發(fā)現(xiàn)這種項目初始環(huán)境既然是相同的,可以賦予它模板的概念,那么是不是可以再動手寫一個屬于自己團隊的開發(fā)腳手架(CLI),用于初始化項目?