前回までのあらすじ
Express を TypeScript で書いて webpack で build する構成で作成していて、環境毎に切り替えたい情報を直接バンドルして build したいと思いやって見たメモ。
WHY?
webpack をビルドする際に mode
や process.env
で環境変数を渡せるので、例えばこんな風に process.env.NODE_ENV
を利用して読み込む環境変数を切り替えようとしていました。
import * as dotenv from 'dotenv'; if (process.env.NODE_ENV === 'production') { dotenv.config({ path: path.join(__dirname, '.env.prod')}); } else { dotenv.config({ path: path.join(__dirname, '.env.dev')}); }
しかし、build されたコードを見ていると process.env
はそのままだったので、実行環境でも process.env
に変数を渡す方法でアプリを起動させる必要がありそうでした。なので環境変数を直接バンドルしてしまえば実行環境の設定が楽になりそう。(セキュリティに関わる値は外から直接見れないようにした .env
とかを使うのが良いのだとは思いますが)
構成
/app |- /dist # build されたアプリの出力先 |- /src | |- index.ts # express entrypoint |- /webpack | |- base.config.js # 共通設定 | |- dev.config.js | |- prod.config.js |- nodemon.json |- package.json |- tsconfig.json
- 開発中は nodemon で ts-node を実行して src 内の TypeScript を監視して開発
- development / production それぞれのモードで build して確認できるようにする
webpack resolve.alias を使ってファイルをバンドルする
resolve.alias
にファイルの場所を設定するとスクリプトからはエイリアスのプロパティ名をルートとして読み込めるようになる。
// webpack.config.js const path = require('path'); module.exports = { //... resolve: { alias: { Utilities: path.resolve(__dirname, 'foo/bar/utilities/'), } } };webpack の対象スクリプトでは `/foo/bar/utilities/` を `Utilities/` で import できるようになる
import MyUtility from 'Utilities/my-utility';cf. Resolve | webpack
webpack.config を dev / prod モード別で使い分けているので、それぞれで別の設定ファイルを alias として読み込ませるようにすれば環境に合わせて別の設定ファイルを import でバンドルさせることができる。
環境変数ファイルの作成
$ mkdir .env $ touch .env/dev.config.ts # Development 用 $ touch .env/prod.config.ts # Production 用
設定ファイルはそれぞれ同じプロパティを作成しておく
.env/dev.config.ts
export default { ENV_MODE: 'development', PORT: 3000, };
.env/prod.config.ts
export default { ENV_MODE: 'production', PORT: 8080, };
webpack resolve.alias の作成
共通で使用している webpack/base.config.js
で実行時の process.env を使って alias を動的に設定する
webpack/base.config.js
const node_env = process.env.NODE_ENV || 'dev'; module.exports = { // … resolve: { alias: { //... UserEnv$: path.resolve(__dirname, `../.env/${node_env}.config.ts`), } } };
npm script で NODE_ENV を渡すようにする
環境変数のパスを作成するのに NODE_ENV
の値を利用するようにするので、build 用の npm-script から変数を渡すようにします。
package.json
"scripts": { "build:dev": "NODE_ENV=dev webpack --config ./webpack/dev.config.js", "build": "NODE_ENV=prod webpack --config ./webpack/prod.config.js", }
メインファイルで alias 指定した設定ファイルを import する
/src/index.ts
//@ts-ignore const USER_ENV = require('UserEnv').default; const PORT = USER_ENV.PORT || 3000; // … app.listen(PORT, () => { console.log(`Mode: ${USER_ENV.ENV_MODE}`); console.log(`listening on port http://localhost:${PORT}`); });
本当は型定義ファイルを作るのが正しいのだと思うけど、まだ理解が足りないので //@ts-ignore
で lint を誤魔化し…
$ npm run build:dev
のときは Mode: development
と表示され localhost:3000
,
$ npm run build
のときは Mode: production
と表示され localhost:8080
で動作していれば OK
nodemon で実行する開発モードでも設定ファイルを読み込ませるようにする
ここまでで webpack を使って build する際には alias を使った設定ファイルを読み込ませることが出来るようになりましたが、開発モードでは webpack を通さずに nodemon + ts-node のホットリロードで実行させるようにしているので UserEnv
が見つからずエラーになってしまうので、.env/dev.config.ts
を UserEnv
として読み込めるようにする必要があります。
tsconfig-paths を使って tsconfig に alias を作成する。
tsconfig-paths
というライブラリを使うと tsconfig で alias import が出来るようになるみたいです!
tsconfig.json
に alias を設定します。
tsconfig.json
{ // ... "paths": { // webpack の resolve.alias で設定したエイリアス名と同じ名前で登録する "UserEnv": ["../.env/dev.config"] } }
tsconfig の paths
を alias import するには ts-node に -r tsconfig-paths/register
オプションが必要なようなので、nodemon.json の実行コマンドを編集します
nodemon.json
{ - "exec": "ts-node ./src/index.ts" + "exec": "ts-node -r tsconfig-paths/register ./src/index.ts" }
これで nodemon -L
で開発モードを実行した時は require('UserEnv').default
で .env/dev.config.ts
が読み込まれるようになりました!
₍ ᐢ. ̫ .ᐢ ₎ ヤッタネ!
TypeScript の設定ファイルなので、webpack で build する際にも読み込まれてエイリアスがコンフリクトするのでは?と思ったのですが、webpack で build する際は -r tsconfig-paths/register
のオプションの指定がないので webpack の resolve.alias
が読み込まれます。ヨシ!
所感
実務で環境を作った知見が無いので、これが本当に良いやり方なのか分かってない部分がありますが、とりあえず自分の思う実現したかったことは実現することができました!nodemon から global 変数を渡せる方法が分かれば webpack の DefinePlugin で変数を渡す方が美しい気がしていますが…
[参考]
- Resolve | webpack
- Webpack で ビルド時、環境ごとに読み込む環境変数を分けたい - Qiita
- TypeScriptでaliasなパスでmoduleをimportできるように - Qiita
- ts-node で実行時に tsconfig.json の paths(alias) 設定が効かない - Qiita
- TypeScript で型定義ファイル( d.ts )がないときの対処法 - Qiita
- dotenvとcross-envで環境変数を設定して開発環境の処理を切り替える | Tips Note by TAM
- アーティスト:さよならポニーテール
- 発売日: 2012/05/23
- メディア: CD