[guide] Monorepo with React and TS
這個 template 的功能包含:
- 編譯 React 元件
- 支援 TypeScript 的編譯
- ESLint with TypeScript
- 可以撰寫測試
- 透過 lerna 進行版本控制與專案管理
- 透過 rollup 將專案打包
初始化 lerna
lerna init & lerna create @ pjchender/mono-react-ts-template
$ lerna init
create packages
$ lerna create @mono-react-component-template/core -y
$ lerna create @mono-react-component-template/components -y
Setup TypeScript
setup tsc and build with tsc @ pjchender/mono-react-ts-template
- 在根目錄建立
tsconfig.settings.json,這支檔案是給packages/*中的專案使用的 - 在
packages/*中各自建立tsconfig.json,並且extends來自外層的tsconfig.settings.json - 在根目錄建立
tsconfig.json,透過在此檔案中設定references來將packages/*中的tsconfig.json關聯起來 - 這時候的做法是透過
tsc來打包專案,但後續會改成使用 babel 搭配@babel/preset-typescript來編譯 TypeScript
Add cleanup script
add clean script to remove dist @ pjchender/mono-react-ts-template
每次專案打包後都會產生 dist 和 *.tsbuildinfo 的檔案,透過 rimraf 這個套件來快速移除這些檔案。
- 安裝
rimraf - 在
packages/*及根目錄的package.json建立對應的clean指令
Setup ESLint with TypeScript
-
安裝和 ESLint、TypeScript 及 React 有關的套件
npm install -D eslint @typescript-eslint/eslint-plugin @typescript-eslint/parsernpm install eslint-config-airbnb-typescript \eslint-plugin-import@^2.22.0 \eslint-plugin-jsx-a11y@^6.3.1 \eslint-plugin-react@^7.20.3 \eslint-plugin-react-hooks@^4.0.8 \@typescript-eslint/eslint-plugin@^4.4.1 \eslint-config-prettier \eslint-plugin-prettier \eslint-plugin-simple-import-sort \--save-devnpm install --save-dev --save-exact prettier -
在根目錄建立
.eslintrc.js,在packages/*中的 eslint 會繼承這支檔案的設定 -
在
packages/*中建立.eslintrc.js,並透過extends繼承根目錄的.eslintrc.js -
建立
.prettierrc -
建立
.eslintignore -
在
packages/*及根目錄的package.json建立對應的lint指令
Testing (Jest)
setup babel for testing(jest) @ pjchender/mono-react-ts-template
-
安裝所需的套件
npm install jest \@types/jest \eslint-plugin-jest \@babel/preset-env \@babel/preset-typescript \--save-dev -
在根目錄建立
babel.config.js,Jest 在執行時預設就會讀這支的設定 -
在
packages/*中各自建立.babelrc並且繼承根目錄的babel.config.js檔 -
在
packages/*及根目錄的package.json建立對應的test指令
rollup
setup rollup with babel to build ts files @ pjchender/mono-react-ts-template
安裝所需的套件
npm install rollup \
@rollup/plugin-node-resolve \
@rollup/plugin-commonjs \
@rollup/plugin-babel \
rollup-plugin-terser \
@babel/plugin-transform-runtime \
--save-dev
編譯 TypeScript
一般來說,最常使用的方式是使用 Babel 來編譯 TypeScript 檔案,搭配 tsc 來檢驗型別並產生 .d.ts 檔案。這裡我們也選擇這樣做。
-
建立
rollup.config.js@rollup/plugin-node-resolve:在沒有使用 node-resolve 時,rollup 不會去把 node_modules 的內容 resolve 出來並放到 bundle 中,但若使用了 resolve,這時就會把 node_modules 內的結果直接放到 bundle 中。@rollup/plugin-babel:使用 babel@rollup/plugin-commonjs:將 CommonJS 的 modules 轉成 ES6 modules,來讓 rollup 進行 bundlerollup-plugin-terser:將 bundle 的檔案做 minify
-
由於要讓 Babel 能夠編譯
.ts檔,需要先透過node-resolve找到.ts的位置(透過extensions的設定) -
接著一樣透過
extensions的設定讓 Babel 能夠處理 ts 檔 -
在
packages/*及根目錄的package.json建立對應的build指令
要選擇使用 Babel 或 TSC 來編譯 TypeScript 的檔案可以參考:Setup TypeScript Template @ pjchender
透過 tsc 產生 type definition
use tsc to build .d.ts files @ pjchender/mono-react-ts-template
由於透過 Babel 編譯 TypeScript 時,並不會產生對應的 .d.ts 檔,此時其他開發者使用此套件時,並不會獲得任何的型別資訊(及限制),因此我們仍需要使用 tsc 來產生 .d.ts 檔。
-
若要使用 tsc 來做型別檢查並產生對應的
.d.ts檔,記得在 tsconfig.json 中的設定要:// tsconfig.json// https://www.typescriptlang.org/docs/handbook/babel-with-typescript.html"compilerOptions": {// Ensure that .d.ts files are created by tsc, but not .js files"declaration": true,"emitDeclarationOnly": true,// Ensure that Babel can safely transpile files in the TypeScript project"isolatedModules": true} -
在
package.json中要加上types這個欄位,讓使用此套件的開發者可以正確載入到 Type Definition// package.json{"name": "@mono-react-component-template/components","version": "0.0.0",+ "main": "dist/index.cjs.js",+ "module": "dist/index.esm.js",+ "types": "dist/index.d.ts","directories": {"src": "src","test": "__tests__"},} -
在
packages/*及根目錄的package.json建立對應的build:ts指令
設定進版和發佈的指令
- 透過
lerna version patch的指令更新版號,預設的情況下,lerna 會和 git remote 的位址有連動,若不需要,可以加上--no-git-tag-version - 這裡我們會發到由 verdaccio 所建立的本機的 npm registry,因此會把 publish 的 registry 設為
--registry 'http://localhost:4873
diff --git a/package.json b/package.json
index ac740dc..40aa653 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,9 @@
"build:ts": "lerna run build:ts",
"lint": "lerna run lint",
"clean": "lerna run clean",
- "test": "lerna run test"
+ "test": "lerna run test",
+ "version:patch": "lerna version patch --no-git-tag-version",
+ "publish:local": "lerna run build && lerna publish from-package --registry 'http://localhost:4873'"
},
"devDependencies": {
"@babel/plugin-transform-runtime": "^7.14.3",
編譯 React 元件
setup to build react components @ pjchender/mono-react-ts-template
安裝 React 套件
在 package/components 專案中安裝 react 及相關套件,並且一併放到 peerDependencies 中:
# 由於有在 npm 設定 workspace 所以可以透過 --workspace 進行安裝
npm install -D react @types/react styled-components @types/styled-components --workspace=packages/components
安裝 @babel/preset-react
在還沒有安裝對應的 babel plugin 就要編譯 react 元件時,因為 rollup 不認得 JSX,因此會發生錯誤:

npm install -D @babel/preset-react
並在 babel.config.js 中建立對應的設定檔:
diff --git a/babel.config.js b/babel.config.js
index 97ed664..406d237 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -8,6 +8,12 @@ module.exports = {
},
},
],
+ [
+ '@babel/preset-react',
+ {
+ runtime: 'automatic',
+ },
+ ],
'@babel/preset-typescript',
],
plugins: ['@babel/plugin-transform-runtime'],
修改 eslintrc 的設定
+ peerDependencies: true,
packageDir,
},
Testing for React Component
setup for testing react components @ pjchender/mono-react-ts-template
-
在
packages/components中安裝對應的套件(這裡我們假設packages/core不會用到 react,如果會的話,或許可以把@testing-library裝在根目錄):npm install -D @testing-library/react \@testing-library/jest-dom \@testing-library/react-hooks \@testing-library/user-event \@types/styled-components \react-test-renderer -
在根目錄建立
jest.config.js,設定testEnvironment為jsdom,並且使用'@testing-library/jest-dom/extend-expect'// jest.config.jsprocess.env.TZ = 'UTC';module.exports = {testEnvironment: 'jsdom',setupFilesAfterEnv: ['@testing-library/jest-dom/extend-expect'],}; -
在
packages/*中建立jest.config.js並把根目錄的設定 import 進來
Setup Styled-Components
setup styled-components for babel and jest @ pjchender/mono-react-ts-template
babel plugin
babel-plugin-styled-components @ styled-components
使 用 babel-plugin-styled-components:
- 透過
ssr的設定,避免 client 端和 server 端產生的 className 名稱不同 - 透過
displayName的設定,把元件的名稱顯示在 className 中方便 debug - Minification:可以透過
minify的設定將空白和註解移除;透過transpileTemplateLiterals只留下有用的值 - Dead Code Elimination:透過
pure的設定,會在每一個 styled-components 和 helper 前加上#__PURE__的 comment,以變其它 minifier 可以解決這個問題
npm install --save-dev babel-plugin-styled-components
Jest Styled Components
Jest Styled Components @ Github
- 使用
jest-styled-components,它可以把 Styled Components 的樣式產生 snapshot 中,讓開發者看到是什麼樣式改變了,而不會只是看到一堆 className。 - 可以透過在
jest.config.js中透過setupFilesAfterEnv: ['jest-styled-components']的設定來套用這個套件。 - 如果是 monorepo 的話,一定要安裝在
packages/*內,不能只是裝在根目錄,否則會沒有效。
- 如果是 monorepo 的話,一定要安裝在
packages/*內,不能只是裝在根目錄,否則會沒有效(不會看到 CSS 樣式被抽出在 snapshot testing 中,而是只會看到 className)。 - 如果 Jest Styled Components 沒有作用,還有一個原因是載入了多個 styled-components 的 instance!(參考:Working with multiple packages),解決的方式則可以參考 Why am I getting a warning about several instances of module on the page?