蒼時弦也
蒼時弦也
資深軟體工程師
發表於

專案設定 - Clean Architecture in TypeScript

這篇文章是 Clean Architecture in TypeScript 系列的一部分,你可以透過 Leanpub 提前閱讀內容。

這一系列會預設在 Cloudflare 相容的環境下運行,在前端開發的部分會以 Vite 搭配 Hono 來使用,這幾個工具有很不錯的相容性,很適合在這次的情境中使用。

初始化專案

首先,我們使用 Cloudflare 的 Wrangler 來初始化專案,建立一個以 Hello World 為樣板,選擇「Worker only」並且使用 TypeScript 的新專案。

1npx wrangler init

大部分樣板的初始設定不會差異太大,但我們使用比較簡單的 Worker only 會比較容易繼續後續的處理。

接下來我們要把 Vite、Hono、Hono 的 Vite Dev Server 加入到專案裡面。

1npm install -D vite @hono/vite-dev-server
2npm install hono

接下來,先暫時將 src/index.ts 替換為以下內容,確保 Hono 運作正常。

1import { Hono } from 'hono';
2
3const app = new Hono();
4
5app.get('/', (c) => c.text('Hello World!'));
6
7export default {
8	fetch: app.fetch,
9} satisfies ExportedHandler<Env>;

因為 Worker only 樣板預設會對 Hello World! 的回傳進行測試,可以運行 npm run test 快速驗證修改。

接下來加入 vite.config.mts 設定使用 Vite 運行開發環境。

 1import devServer from "@hono/vite-dev-server";
 2import cloudflareAdapter from "@hono/vite-dev-server/cloudflare";
 3import { defineConfig, Plugin } from "vite";
 4
 5export default defineConfig(() => {
 6	return {
 7		plugins: [
 8			devServer({
 9				entry: "./src/index.ts",
10				adapter: cloudflareAdapter,
11			}),
12		] as Plugin[],
13	};
14});

Hono 已經幫我們準備好對應 Cloudflare 環境的 Adapter 因此會需要在 Vite 的 devServer 中額外設定,指定到我們要的檔案上。

最後更新 package.ts 將原本的 wrangler dev 替換為 vite 就可以用 npm run dev 看到使用 Vite 的版本。

前端設定

我們要使用 Hono 同時來開發前端跟後端,這也是採用 Vite 的理由之一,因此我們還需要更近一步做設定,讓前後端都能夠被處理。

加入新的套件,用於處理前端的建置以及使用 TailwindCSS 作為本次前端的外觀設計。

1npm install -D @hono/vite-build
2npm install tailwindcss @tailwindcss/vite

接下來修改 vite.config.mts 為以下的狀態,根據 Hono 的文件,我們需要區分當下建置的是前端還是後端,並且給予不同的設定,不過在開發模式下只需要後端的即可,因此預設回傳後端的設定。

 1import build from "@hono/vite-build/cloudflare-workers";
 2import devServer from "@hono/vite-dev-server";
 3import cloudflareAdapter from "@hono/vite-dev-server/cloudflare";
 4import tailwindcss from "@tailwindcss/vite";
 5import { defineConfig, Plugin } from "vite";
 6
 7export default defineConfig(({ mode }) => {
 8	if (mode == "client") {
 9		return {
10			build: {
11				rollupOptions: {
12					input: ["./src/client.tsx"],
13					output: {
14						entryFileNames: "public/app.js",
15						chunkFileNames: "public/assets/[name]-[hash].js",
16						assetFileNames: "public/assets/[name].[ext]",
17					},
18				},
19				emptyOutDir: false,
20				copyPublicDir: false,
21			},
22			plugins: [tailwindcss()],
23		};
24	}
25
26	return {
27		plugins: [
28			devServer({
29				entry: "./src/index.tsx",
30				adapter: cloudflareAdapter,
31			}),
32			build({
33				entry: "./src/index.tsx",
34			}),
35			tailwindcss(),
36		] as Plugin[]
37	};
38});

接著我們要修改 tsconfig.json 加入對應的設定,調整以下參數。

1{
2	"compilerOptions": {
3		"jsx": "react-jsx",
4		"jsxImportSource": "hono/jsx",
5	}
6}

將 JSX 的來源指定為 hono/jsx 用來代替原生的 React 元件。

1{
2	"compilerOptions": {
3		"types": [
4			"@cloudflare/workers-types/2023-07-01", "vite/client"
5		],
6	}
7}

將 Vite 的型別定義加入到 types 中,新版的 Cloudflare Worker 可以不再依賴 workers-types 這邊可以考慮將其移除,短時間內不影響開發。

1{
2	"include": ["worker-configuration.d.ts", "src/**/*.ts", "src/**/*.tsx"]
3}

原本只會將 .ts 包含進來,因為使用了 JSX 所以需要把 .tsx 檔案也一起涵蓋進去。

接下來將原本的 src/index.ts 改為 src/index.tsx 加入空白的前端頁面。

 1import { Hono } from 'hono';
 2
 3const app = new Hono();
 4
 5app.get("/", (c) =>
 6	c.html(
 7		<html lang="zh-TW">
 8			<head>
 9				<title>Clean Architecture in TypeScript</title>
10				<meta charSet="utf-8" />
11				<meta content="width=device-width, initial-scale=1" name="viewport" />
12				{import.meta.env.PROD ? (
13					<script type="module" src="/app.js" />
14				) : (
15					<script type="module" src="/src/client.tsx" />
16				)}
17			</head>
18			<body>
19				<div id="root" />
20			</body>
21		</html>,
22	),
23);
24
25export default {
26	fetch: app.fetch,
27} satisfies ExportedHandler<Env>;

上述是 Hono 比較特殊的用法,我們可以直接在後端使用 JSX 語法撰寫 HTML 且能夠正常運作,如果專案比較單純的話,是一個不錯的方案。

加入新的 src/client.tsx 放入簡單的 console.log("Hello World") 我們就可以透過 npm run dev 看到畫面變成空白,並且在開發者工具的 Console 可以看到 Hello World 的訊息。

最後,更新 wrangler.jsonc 將以下這行註解取消,新專案還需要注意前一個設定尾端是否有 ,

1{
2	"assets": { "directory": "./public/", "binding": "ASSETS" },
3}

這樣一來我們在開發跟部署就能夠都使用 Hono 來進行,其他像是 vite-tsconfig-paths、Pritter 等套件,則可以看需求安裝。

依賴注入

因為我們選用 tsyring 作為依賴注入框架的關係,還需要額外啟用一些 TypeScript 的特性才可以使用。

先安裝依賴注入相關的套件。

1npm install tsyringe reflect-metadata

tsconfig.json 啟用實驗性的功能

1{
2	"compilerOptions": {
3		"experimentalDecorators": true,
4		"emitDecoratorMetadata": true
5	}
6}

最後在 src/index.tsx 第一行加入 Reflect Metadata 的引用。

1import "reflect-metadata";
2
3// ...

這個引用必須放在第一行,才不會在後續編譯後因為找不到引用的設定而無法正常運行專案。到此為止,我們將整個專案必要的設定都處理完畢,可以開始進入開發階段。

上述的專案設定大多是可以轉換為樣板的,如果認為經常會採用這樣的做法,以使用樣板機制為前提,撰寫成 AI 提示(Prompt)反而會降低成功率,AI 比較適合在針對細部微調的情境採用。