NativeScript-Vueで"環境変数"を環境ごとの設定ファイルで管理したい

NativeScript-Vueで、本番・開発などの環境ごとに、各種設定値（APIシークレットなど）を変更したく、環境ごとに設定ファイルを作成して管理したくなったので、やったことをメモします。

# 環境

* コーディング環境のOS: Windiws 10
* Node: 14.15.0
* NPM: 6.14.8
* NativeScript CLI: 7.0.11
* @nativescript/core: 7.0.13

# 1. 環境別の設定ファイルを作る

jsファイルで作成します。置き場所はどこでも良いですが、今回はルートに`.env`ディレクトリを作って、その中に、環境別の設定ファイルを用意しました。

環境変数をJSモジュールとして定義するので、場合に応じて、下記例のようにオブジェクトのキーを多階層にしても良いと思います。また、原則として、どの環境でもオブジェクトの構造は同一となるようにします。

開発環境(dev):

```js:.env/dev.js
module.exports = {
    EXAMPLE: {
        SECRET_KEY: '123456789dev'
    },
    SAMPLE: {
        APP_NAME: 'dev_app',
        USER_LIST: ['admin', 'test_user', 'user_001'],
    }
}
```

本番環境(prod):

```js:.env/prod.js
module.exports = {
    EXAMPLE: {
        SECRET_KEY: '123456789prod'
    },
    SAMPLE: {
        APP_NAME: 'prod_app',
        USER_LIST: ['admin'],
    }
}
```

はたまたhogehoge環境(hogehoge):

```js:.env/hogehoge.js
module.exports = {
    EXAMPLE: {
        SECRET_KEY: '123456789hogehoge'
    },
    SAMPLE: {
        APP_NAME: 'hogehoge_app',
        USER_LIST: ['fugafuga'],
    }
}
```

# 2. ビルド時に適切な設定ファイルを読み込む

NativeScriptで、デバッグやビルドをするときは、`tns ～～`のコマンドを使うと思いますが、その際に、環境名を指定することで、適切な環境変数設定ファイルを読み込むようにします。また、任意のスクリプト内で、`process.env`から、環境変数の設定値を読めるようにします。

まず、ルートにある`webpack.config.js`に追記をします。

```js:webpack.config.js（必要箇所のみ抜粋）
module.exports = => {
    const config = {
        plugins: [
            new webpack.DefinePlugin({
                "global.TNS_WEBPACK": "true",
                "global.isAndroid": platform === 'android',
                "global.isIOS": platform === 'ios',
                "TNS_ENV": JSON.stringify(mode),
                "process": "global.process",
                // ↓↓↓　追記
                "process.env": env && env.envtype ? JSON.stringify(require('./.env/' + env.envtype + '.js')) : "null",
                // ↑↑↑　追記
            }),
        ],
    };
}
```

DefinePluginを使い、任意のスクリプトから呼ばれる`process.env`に、環境変数モジュールを注入しています。DefinePluginは、キー・値ともに、JSのコードを文字列で指定する必要がありますので、`require()`で読み込んだモジュールを、`JSON.stringify()`で文字列化させています。

どの設定ファイルを読み込むかは、`require()`の引数で指定しています。`env.envtype`の中に、環境名が格納されている想定です。この環境名はいつ指定するかというと、`tns ～～`コマンド実行時です。

なお、`env.envtype`が指定されていない場合は、上記例では`null`をセットするようにしています。

#3. 動作確認

以上で、`process.env`を使って、環境変数を読みに行くことができます。

例えば、`src/main.js`（TypeScriptの場合は`src/main.ts`）に、以下のような記述をしておきます。

```js:src/main.js
console.log('アプリ名:' + process.env.SAMPLE.APP_NAME);
```

そして、デバッグやビルド時に、引数として`--env.envtype {環境名}`を指定します。

例えば、開発環境の設定ファイルを使ってデバッグしたい場合は

```shell
tns debug android --device EMULATOR001 --env.envtype dev
```

というように引数を指定して実行します。

すると、コンソールには、

```
アプリ名:dev_app
```

と表示されます。

同様に、

```shell
tns debug android --device EMULATOR001 --env.envtype hogehoge
```

の場合は、

```
アプリ名:hogehoge_app
```

と表示されます。

# 4. gitignoreしておく

gitを使用している場合は、環境変数設定ファイルをバージョン管理してしまうとマズい場合が一般的だと思いますので、バージョン管理から除外しておきます。

```:.gitignore
### ↓↓↓　追記
# 環境変数ディレクトリ
.env
### ↑↑↑　追記
```

# 5. 型定義しておく(TypeScript)

TypeScriptを使用している場合は、`process.env`の型を詳細に定義しておくことで、コード補完などができ、便利です。（というか、`process.env`にはもともと`NodeJS.ProcessEnv`という型が定義されており、これは文字列型を値とする辞書型で定義されてるので、型定義しておかないと、今回の例のように複雑な構造で環境変数を定義している場合は型エラーとなります。）

```typescript:types/env.d.ts
declare var TNS_ENV: string;

// ↓↓↓ 追記
declare namespace NodeJS {
    interface ProcessEnv {
        /**
         * 現在の実行環境
         */
        readonly NODE_ENV: 'development' | 'production' | 'test';

        /**
         * EXAMPLEに関する設定
         */
        readonly EXAMPLE: {
            /**
             * シークレットキー
             */
            readonly SECRET_KEY: string;
        };

        /**
         * SAMPLEに関する設定
         */
        readonly SAMPLE: {
            /**
             * アプリケーション名
             */
            readonly APP_NAME: string;
            /**
             * ユーザリスト
             */
            readonly USER_LIST: string[];
        };
    }
}
// ↑↑↑ 追記
```

上記の例にある、`NODE_ENV`は、今回の環境変数云々とは関係なく、あらかじめ用意されている変数です。

型定義をすると、テキストエディタでは、以下のように補完されます。

![コード補完](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84550/b563001e-1925-188e-b86e-c619a5d086bb.png)

![Doc](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84550/c33d294f-45bc-b838-546d-ffabd631db11.png)

# 参考

* [stackoverflow: What is the best ways to set up different environment variables for NativeScript with Angular?
](https://stackoverflow.com/questions/51450230/what-is-the-best-ways-to-set-up-different-environment-variables-for-nativescript)
* [Qiita: Webpack で ビルド時、環境ごとに読み込む環境変数を分けたい](https://qiita.com/syoimin/items/3dac2626f0a4e240ee55)
* [TypeScriptの型: 環境変数 (process.env) 用の型情報を定義する](https://maku.blog/p/r8iry9g/)