Node.jsのexpress(TypeScript版)でc言語のプログラムをRenderで実行

# はじめに
さきほど、JavaScript版のexpressでc言語のプログラムを実行するのを書いたばかりですがその続編で、TypeScript版をお届けします。

https://qiita.com/yo16/items/108de9889c79313df005

この記事は、c言語とかRenderとかはあまり書かず、JavaScriptでexpressを作ったのと同じことをTypeScriptでやるにはどうするかっていう、基礎に立ち戻った感じになりそうです。

c言語のコンパイルは、JavaScriptのままとします。（そこをTypeScriptにこだわる必要性を感じなかったので）

githubのリポジトリは、JavaScriptと同じで、フォルダを「app-c-lang2-ts」にしました。こちらもRenderで動作確認済み。

https://github.com/yo16/gcc_render_prac/tree/main


# 手順
## 1. 初期処理
```sh
mkdir app-c-lang2-ts
cd app-c-lang2-ts
npm init -y
npx tsc --init
npm install express
npm install --save-dev @types/express
```
ここから！？っていう感じですが、基礎なので。

## 2. サーバーをコーディング
expressサーバーです。
```TypeScript:/server/app.ts
import express, { Request, Response } from "express";
import path from "path";
import { exec } from "child_process";

const app = express()
const port = 3000

app.get('/', (req: Request, res: Response) => {
    const outputFileName = 'hello' + (process.platform === 'win32' ? '.exe' : '');
    const buildOutputPath = path.join(__dirname, outputFileName);

    exec(buildOutputPath, (runError, stdout, stderr) => {
        if (runError) {
            console.error(`実行エラー: ${runError}`);
            return res.status(500).send(`実行エラー: ${runError}`);
        }
        if (stderr) {
            console.error(`標準エラー出力: ${stderr}`);
            return res.status(500).send(`標準エラー出力: ${stderr}`);
        }
        res.send(stdout);
    });
})

app.listen(port, () => {
    console.log(`Example app listening on port ${port}`)
})
```
JavaScriptとの違いは、`app.get('/', (req: Request, res: Response) => {`と型定義しただけです。そのための`import`と。
あ、ESMになっているので、requireじゃなくてimportに変えてます。

## 3. package.jsonでビルドと実行の仕方を指示
`npm run build`で行うことは３つ
1. 依存するモジュールをインストール
1. TypeScriptをJavaScriptへ変換して、`dist`ディレクトリへ出力
1. c言語ソースをコンパイルして、`dist`ディレクトリへ出力

`npm run start`では、`dist`ディレクトリに出力された`app.js`(JavaScript)を実行する。
```json:package.json（一部）
  "scripts": {
    "start": "node ./dist/app.js",
    "build": "npm install && npx tsc && node ./scripts/build-with-c.js"
  },
```
TypeScriptを直接実行するわけでなく、`dist`に出力されたJavaScriptを実行するんだということがわかっていれば、難しい話ではないですが、たぶん初心者は躓くと思います。そのため、c言語のモジュールも`dist`ディレクトリへコピーします。（次）

## 4. TypeScriptの出力先を設定
```JSON:tsconfig.json
～～省略～～
  "outDir": "./dist",
～～省略～～
```
`outDir`という項目に、`./dist`を書いておきます。
そうすると、`dist`フォルダに出力されます。

## 5. c言語ソースのコンパイルスクリプト
ここは、JavaScript版と比べて処理は変更していないので、その話は割愛します。[JavaScript版](https://qiita.com/yo16/items/108de9889c79313df005#c%E8%A8%80%E8%AA%9E%E3%82%BD%E3%83%BC%E3%82%B9%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%AB)をご覧ください。
コンパイル後のコピー先である`buildOutputFilePath`を`dist`にしている点だけが変更点です。
```JavaScript:build-with-c.js
const { exec } = require('child_process');
const { copyFileSync } = require('fs');
const { platform } = require('os');

// コンパイルするCプログラムのパスと出力ファイル名
const scriptsDirName = "scripts";
const inputFilePath = `./${scriptsDirName}/hello.c`;
const outputFileName = 'hello' + (platform() === 'win32' ? '.exe' : '');
const outputFilePath = `./${scriptsDirName}/${outputFileName}`;
const buildOutputFilePath = `./dist/${outputFileName}`;

// Cプログラムをコンパイル
exec(`gcc ${inputFilePath} -o ${outputFilePath}`, (compileError) => {
    if (compileError) {
        console.error(`コンパイルエラー: ${compileError}`);
        process.exit(1);
    }
    console.log('Cプログラムのコンパイル成功');

    // コンパイルしたプログラムをビルドディレクトリにコピー
    copyFileSync(outputFilePath, buildOutputFilePath);
    console.log('ビルドディレクトリにhelloをコピー');
});
```
ここもTypeScriptにする必要性ってあるのかな。c言語のソースが複雑になっても、ここが複雑になるわけではないし、型をしっかり確認したいこともあんまりないような。
そしてTypeScriptにすることで、`package.json`などの設定系の変更が必要になり、そっちが複雑になることを避けました。

以上です。

# おわりに
前回のJavaScript版の"おわりに"に書いた通り、やはりJavaScriptで実装した後にTypeScriptで書き直せば、そんなにハマることはありませんでした。

あえていえば私は、最初、c言語のコンパイルもTypeScriptでやろうとして、「あーもう！」ってなってました。なので今回はいろいろ言い訳をして、そこは見ないことにしたらうまくいきました。🙈

次のステップがあるとしたら、ここまではできたから、次は「c言語のコンパイルをTypeScript」ということだけに着目すればよくなって、問題もすっきりして、案外簡単に解決できるかもしれないです。でも私はいまのところ、メリットを感じないのでやりませんが。