Kintone-cliでKintone-UI-Component+React+Typescript+Firebaseのアプリカストマイズ

今回はKintone-cliを使用してKintone-UI-Component+React+Typescript+Firebaseのアプリのカストマイズを作成したいんです。

## [kintone-cli](https://qiita.com/iShinkai/items/304f0345e12d69d2e0a7)
`kintone-cli`を使用するのでインストールします：

```cli:PowerShell
$ npm install -g git://github.com/kintone/kintone-cli.git
```

### プロジェクトの初期化
`kintone-cli init`でプロジェクトを初期化します、対話式で下記の質問されます：
- **`Project name`**：プロジェクト名を決めて入力し、
- **`Version`**：バージョン番号を決めて入力し、
- **`Description`**：プロジェクトの詳細を入力し、
- **`Author`**：プロジェクトの作者を入力し、
- **`License`**：ライセンスの種類を入力し、
- **`Do you want to use @kintone/kintone-ui-component?`**（_kintone UI Component_を利用するかどうか：
    利用したいから「Y」を入力し、
- **`Do you want to use @kintone/kintone-js-sdk?`**（_kintone JS SDK_を使用するかどうか）：
`@kintone/kintone-js-sdk`は既にdeprecatedですので「n」を入力し、現在の最新のkintoneのSDKを使いたっかった後から`@kintone/rest-api-client`を手動で追加すれば良いです。

```cli:PowerShell
$ kintone-cli init
Welcome to kintone-cli!
Please, input below information so we can get started!
? Project name user-management
? Version 0.0.1
? Description App to manage users
? Author LETEXIER Loic
? License Copyrights Meister Guild 2020
? Do you want to use @kintone/kintone-ui-component? Yes
? Do you want to use @kintone/kintone-js-sdk? No
```
ここまでの手順で：
- プロジェクトを格納するフォルダを作成し、
- その中に[`package.json`](https://qiita.com/drafts/aff4623fd2b893497dc6/edit#packagejson)ファイルを作成し、
- `git`リポジトリの初期化しました。

### プロジェクトの作成
次は実際にプロジェクトを作成します、対話式で下記の質問されます：
- **`What type of app you want to create ?`**（アプリ向けのカスタマイズ開発か、プラグイン開発か）：アプリ向けを選択し、今回はカスタマイズ開発がしたい、
- **`Do you want to set authentication credentials ?`**（認証情報をセットするかどうか）： 認証を利用したいから「Y」を入力し、
- **`What is your kintone domain ?`**：カスタマイズを適用する先の`kintone`ドメイン（○○.cybozu.com）の指定し、
- **`What is your kintone username ?`**：当該環境にログインするためのユーザー名（私はメールアドレスと一緒にしてた）を入力し、
- **`What is your kintone password ?`**：当該環境にログインするためのパスワードを入力し、
- **`Do you use proxy ?`**（接続にプロキシを使うかどうか）：「n」を入力し、
- **`Do you want to use React ?`**（開発フレームワークとして`React`を使うかどうか）：_kintone UI Component_を使う場合は`React`が必よから「Y」を入力し、
- **`Do you want to use TypeScript ?`**（`TypeScript`を使用するかどうか）：`TypeScript`を使用したいから「Y」を入力し、
- **`Do you want to use webpack ?`**（バンドラーとして`Webpack`を使うかどうか）：使用したいから「Y」を入力し、
- **`What is the entry for webpack ?`**：`Webpack`を使用する際のエントリポイントとなるファイルの名前（「index.tsx」のデフォルトは良かったからそのままにした）を入力し、
- **`What is the app name ?`**（ローカルプレビュー時やビルド・デプロイ時にも必要なアプリのフォルダーの名前）：プロジェクトと同じ名前に入力し、
- **`Do you want to use @cybozu/eslint-config for syntax checking ?`**（サイボウズ謹製の eslint 設定を利用するかどうか）：「n」を入力したけど「Y」を入力してもよいと思います、
- **`What is the app ID ?`**：カスタマイズを適用するアプリの「アプリ ID」を入力し、
- **`What is the scope of customization ?`**（カスタマイズをアプリの利用者全員に適用するか、管理者だけにするか、適用しないか）：「ALL」を選択しました。

```cli:PowerShell
> cd user-management
> kintone-cli create-template
? What type of app you want to create ? Customization
? Do you want to set authentication credentials ? Yes
? What is your kintone domain ? ○○.cybozu.com
? What is your kintone username ? □□@○○.com
? What is your kintone password ? [hidden]
? Do you use proxy ? No
? Do you want to use React ? Yes
? Do you want to use TypeScript ? Yes
? Do you want to use webpack ? Yes
? What is the entry for webpack ? index.tsx
? What is the app name ? user-management
? Do you want to use @cybozu/eslint-config for syntax checking ? No
? What is the app ID ? 123
? What is the scope of customization ? ALL
```
## [@kintone/dts-gen](https://qiita.com/iShinkai/items/f832ffd0436a3630832e#kintone-dts-gen)
`TypeScript`の開発では`JavaScript`で書かれたAPIを使用するために`d.ts`ファイル（型定義ファイル）は大事です。

`@kintone/dts-gen`はこの型定義ファイルを作成してくれるツールです：
>@kintone/dts-genはkintoneのJavaScriptカスタマイズ用の関数定義に加えて、指定したアプリからフィールド情報を取り出すコマンドラインツールが同梱されています。

[`TypeScript`で`kintone`の開発をする](https://developer.cybozu.io/hc/ja/articles/360023293091-TypeScript%E3%81%A7kintone%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%9E%E3%82%A4%E3%82%BA%E9%96%8B%E7%99%BA%E3%82%92%E3%81%97%E3%81%A6%E3%81%BF%E3%82%88%E3%81%86)なら必須なものですのでインストールします：

```cli:PowerShell
$ npm install --save-dev @kintone/dts-gen
```
使い方は：

```
npx kintone-dts-gen --host https://(サブドメイン).cybozu.com
                      -u (ユーザー名)
                      -p (パスワード)
                      --app-id (アプリID)
                      --namespace (名前空間プレフィクス)
                      -o (ファイル出力先)
```
下記のコマンドでアプリのフィールド定義情報を取得して、`d.ts`ファイルを作成します：

```cli:PowerShell
$ npx kintone-dts-gen --host https://mgild.cybozu.com -u l.loic@m-gild.com -p ******** --app-id 123 --namespace userFields.types -o user-management/source/users.fields.d.ts
```
[`d.ts`の型定義ファイル](https://qiita.com/drafts/aff4623fd2b893497dc6/edit#usersfieldsdts)を作成して、エディタ上でその効果を発揮させるためプロジェクトの直下にある[`tsconfig.json`](https://qiita.com/drafts/aff4623fd2b893497dc6/edit#tsconfigjson)で型定義ファイルの参照を指定する必要です：

```file:tsconfig.json
...
    "files": [
        "../node_modules/@kintone/dts-gen/kintone.d.ts",
        "./source/users.fields.d.ts"
    ],
    "exclude": [
        "node_modules",
        "dist"
    ]
...
```
これを TypeScript で参照可能にします。

常に、ビルドしたファイルが格納される`dist`フォルダーを`exclude`に追加します。
## [firebase](https://github.com/firebase/firebase-js-sdk)
このアプリで`firebase`も利用したいのでインストールします：

```cli:PowerShell
$ npm install --save firebase  
```
## CSS
CSSファイルを使用するために`entry`に追加しないといけません：

```file:user-management/user-management/webpack.config.js
...
  entry: [
    path.resolve('user-management/source/index.tsx'),
    path.resolve('user-management/source/css/user-management.css')
  ],
...
```
## ビルド + デプロイ
ビルドします：

```cli:PowerShell
$ kintone-cli build --app-name user-management
```
デプロイします：

```cli:PowerShell
$ kintone-cli deploy --app-name user-management
```
アプリ側にはちゃんとビルドされたファイルが適用されます。

## 結果のファイルの一覧
プロジェクト設定完了！
### プロジェクトフォルダー
#### package.json
```file:user-management/package.json
{
    "name": "user-management",
    "version": "0.0.1",
    "description": "App to manage users",
    "author": "LETEXIER Loic",
    "license": "Copyrights Meister Guild 2020",
    "dependencies": {
        "@kintone/kintone-ui-component": "^0.6.0",
        "firebase": "^7.22.1",
        "react": "^16.8.6",
        "react-dom": "^16.7.0"
    },
    "devDependencies": {
        "@babel/core": "^7.3.3",
        "@babel/plugin-proposal-class-properties": "^7.3.3",
        "@babel/plugin-syntax-dynamic-import": "^7.2.0",
        "@babel/preset-env": "^7.3.1",
        "@babel/preset-react": "^7.0.0",
        "@babel/preset-typescript": "^7.3.3",
        "@kintone/customize-uploader": "^2.0.5",
        "@kintone/dts-gen": "^3.0.5",
        "@types/react": "^16.8.16",
        "@types/react-dom": "^16.8.4",
        "babel-loader": "^8.0.5",
        "core-js": "^3.2.1",
        "css-loader": "^2.1.0",
        "dotenv-webpack": "^3.0.0",
        "local-web-server": "^2.6.1",
        "regenerator-runtime": "^0.13.3",
        "style-loader": "^0.23.1",
        "typescript": "^3.6.3",
        "webpack": "^4.30.0",
        "webpack-cli": "^3.2.3"
    },
    "scripts": {
        "dev": "ws",
        "build-user-management": "webpack --config user-management/webpack.config.js"
    }
```
#### .gitignore
```file:user-management/.gitignore
node_modules
user-management/auth.json
```
### アプリフォルダー

#### webpack.config.js
```file:user-management/user-management/webpack.config.js
const path = require('path');
const config = {
  entry: [
    path.resolve('user-management/source/index.tsx'),
    path.resolve('user-management/source/css/user-management.css')
  ],
  resolve: {
    extensions: ['.ts', '.tsx', '.js'],
  },
  output: {
    path: path.resolve('user-management/dist'),
    filename: 'user-management.min.js',
  },
  module: {
    rules: [
      {
        test: /.ts?$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-typescript'],
          },
        },
      },
      {
        test: /.tsx?$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/react', '@babel/preset-typescript'],
          },
        },
      },
      {
        test: /.css$/,
        use: ['style-loader', 'css-loader'],
      },
    ],
  },
  performance: {
    maxEntrypointSize: 10000000,
    maxAssetSize: 10000000,
  },
};

module.exports = (env, argv) => {
  'use strict';
  if (argv.mode === 'development') {
    config.devtool = 'source-map';
    false;
  }

  if (argv.mode === 'production') {
    //...
  }
  return [config];
};
```
#### tsconfig.json
```file:user-management/user-management/tsconfig.json
{
    "compilerOptions": {
        "typeRoots": [
            "../node_modules/@types",
            "./source/global.d.tsx"
        ],
        "noImplicitAny": false,
        "jsx": "react"
    },
    "include": [
        "source/**/*.ts",
        "source/**/*.tsx"
    ],
    "files": [
        "../node_modules/@kintone/dts-gen/kintone.d.ts",
        "./source/users.fields.d.ts"
    ],
    "exclude": [
        "node_modules",
        "dist"
    ]
}
```
#### config.json
```file:user-management/user-management/config.json
{
    "appID": 123,
    "appName": "user-management",
    "type": "Customization",
    "scope": "ALL",
    "uploadConfig": {
        "desktop": {
            "js": [
                "user-management/dist/user-management.min.js"
            ],
            "css": []
        },
        "mobile": {
            "js": [
                "user-management/dist/user-management.min.js"
            ]
        }
    }
}
```
#### .babelrc
```file:user-management/user-management/.babelrc
{
    "plugins": [
        "@babel/plugin-proposal-class-properties",
        "@babel/plugin-syntax-dynamic-import"
    ],
    "presets": [
        [
            "@babel/preset-env",
            {
                "useBuiltIns": "usage",
                "corejs": {
                    "version": 3,
                    "proposals": true
                }
            }
        ]
    ]
}
```
### ソースフォルダー
#### users.fields.d.ts
```file:user-management/user-management/source/users.fields.d.ts
declare namespace userFields.types {
  interface Fields {
    password: kintone.fieldTypes.SingleLineText;
    email_address: kintone.fieldTypes.SingleLineText;
    user_name: kintone.fieldTypes.DropDown;
    idToken: kintone.fieldTypes.SingleLineText;
  }
  interface SavedFields extends Fields {
    $id: kintone.fieldTypes.Id;
    $revision: kintone.fieldTypes.Revision;
    更新者: kintone.fieldTypes.Modifier;
    作成者: kintone.fieldTypes.Creator;
    レコード番号: kintone.fieldTypes.RecordNumber;
    更新日時: kintone.fieldTypes.UpdatedTime;
    作成日時: kintone.fieldTypes.CreatedTime;
  }
}
```