【nodejsアプリ開発】pkg/Express.js/Svelteでポータブルなバイナリ起動のウェブブラウザアプリを作る


2023/01/06
【nodejsシェルアプリ開発】vercel/pkgでCLI版スネークゲームを作ってみる
蛸壺の技術ブログ|pkg/Express.js/Svelteでポータブルなバイナリ起動のウェブブラウザアプリを作る

以前のブログの内容で、Nodejsアプリをバイナリアプリ化してくれる
『vercel/pkg』(以降、pkg)について触れてみました。

合同会社タコスキングダム|蛸壺の技術ブログ
【nodejsシェルアプリ開発】vercel/pkgでCLI版スネークゲームを作ってみる

vercel/pkgを使って簡単なCLIスネークゲームのバイナリをビルドして動かしてみます。

pkgの魅力はなんと言ってもビルド後のバイナリサイズが小さいことです。

小さければ小さいほどプログラムを使ってもらえるユーザーに配布しやすくなるのがメリットです。

今回はこのpkgの利用を考えていく一例として、
「Express.js」によるローカルサーバー化と、「Svelte」によるSPA(Single Page Application)を組み合わせた、簡単なミニゲームを起動してみるまでを手順化してみます。


合同会社タコスキングダム|蛸壺の技術ブログ【効果的学習法レポート】nodejsをこれから学びたい人のためのオススメ書籍&教材特集

Svelte側のSPAアプリを準備する

pkgとExpress.jsとSvelteのプロジェクトを全てひとまとめにして開発を進めても良いのですが、ここでは、アプリの中身をサッと変えられるように、バックエンドの実行処理側(pkg/Express.js)と、フロントエンド(Svelte)を分離したプロジェクトで別々に管理していきます。

まずはアプリの中身にあたる部分を「Svelte」と「Vite」で作っていきます。なお、SvelteでなくてもAngularやVueやReactなど別のフレームワークで作っても結構です。

以前の回で、
「SvelteとViteでアプリ開発」というネタを紹介していました。

合同会社タコスキングダム|蛸壺の技術ブログ
【Svelte Framework入門】Svelteアプリ開発でrollup.jsからViteに移行する

Svelteアプリ開発環境で、デフォルトのrollupからViteへ移行する手順を解説します。

今回は、そちらの内容を起点にして、適当なSvelteプロジェクトを以下のコマンドでビルドしてみます。

            
            $ yarn build
#もしくは
$ ./node_modules/.bin vite build
        
ビルドするとVite:Svelteでの開発の場合、デフォルトではdistフォルダの中にビルド済みのリソースが固められて出力されています。

            
            $ tree dist/
dist/
├── assets
│   ├── index.501bb487.css
│   ├── index.a7c6f463.js
│   ├── hoge.a93068e7.svg
│   └── piyo.f6732df8.svg
├── favicon.png
└── index.html
        
どのJSフレームワークでもプロダクションビルドするほぼこんな感じで、ここではひとまずビルド出力したリソースファイル一式を準備できれば結構です。

次の節で、このアプリの中身を使うための
「pkg/Express.js」を別プロジェクトで作成していきます。


合同会社タコスキングダム|蛸壺の技術ブログ【効果的学習法レポート】nodejsをこれから学びたい人のためのオススメ書籍&教材特集

バイナリで動くpkg/Express.jsのバックエンドアプリを作成する

こちらが本題で、「ポータブルなpkg/Express.jsのバイナリアプリ」を作成する手順を説明していきます。

pkgのバイナリアプリの作り方は前回詳しく掘り下げました。

合同会社タコスキングダム|蛸壺の技術ブログ
【nodejsシェルアプリ開発】vercel/pkgでCLI版スネークゲームを作ってみる

vercel/pkgを使って簡単なCLIスネークゲームのバイナリをビルドして動かしてみます。

まだpkgの使い方に慣れていない方は是非ともまずそちらを一読ください。

以降ではポイントを絞ってプロジェクトを作成していきます。

pkgプロジェクトの作成

適当なプロジェクトフォルダを作って、以下のようにpackage.jsonを新規追加します。

            
            {
  "name": "pkg-express",
  "version": "0.0.1",
  "description": "To execise to use pkg with Express.js."
}
        
また今回はtypescriptでトランスパイルするので、以下のようにインストールを済ませておきましょう。

            
            $ yarn add typescript tslib -D
        
typescriptの初期化(tsconfig.json)を行います。

            
            $ yarn tsc --init
        
後は、pkg本体をインストールします。

            
            $ yarn add pkg -D
        
インストール後は、package.jsonも以下のように更新しておきましょう。

            
            {
    "name": "pkg-express",
    "version": "0.0.1",
    "description": "To execise to use pkg with Express.js.",
    "bin": "index.js",
    "scripts": {
        "pkg": "pkg",
        "build": "tsc && yarn pkg ."
    },
    "devDependencies": {
        "pkg": "^5.8.0",
        "tslib": "^2.4.1",
        "typescript": "^4.9.4"
    }
}
        

ひとまず準備はこれでOKです。

Express.js用のコードを実装する

pkgアプリを作成した
前回の内容と違うところは、Express.jsを使うところです。

ここでのプロジェクト自体は至ってシンプルに作れますが、サーバー側のプログラムを作成するときに特有の考え方が凝縮されているため、
Express.jsの扱い方自体にバックエンド技術の学習が必要です。

それはさておいて、以前「nexe」でExpress.jsのバイナリアプリ化の話はすでに紹介しておりました。

合同会社タコスキングダム|蛸壺の技術ブログ
【nodejs活用】nexeでポータブルなExpress.jsサーバーを手軽に持ち歩く〜Linux編

Nexeを利用して、nodejsの開発環境なしでもLinuxOS上でコマンドライン一つで立ち上がるモックなExpress.jsサーバーとして使えるツールを作成します。

まずはプロジェクトにExpress.jsを導入します。

            
            $ yarn add express -S
$ yarn add @types/express -D
        
次にindex.tsを新規作成して、コードの中身を以下のように編集します。

            
            import express from "express";
import { join } from 'path';

const endpoint = 'http://localhost:3000';
process.stdout.write(`\x1b[0;32m\x1b[6m👇をCtrl+クリックしてブラウザで開こう!\x1b[0m\n\x1b[0;43;1;37m${endpoint}\x1b[0m`);

const app = express();
const distFolder = join(process.cwd(), 'browser');

app.get('/', (req, res) => {
    res.sendFile(distFolder + '/index.html');
});
app.get(`/*.*`, express.static(distFolder));
app.get(`/assets/*.*`, express.static(distFolder + '/assets/'));
app.listen(3000, () => {});
        
この時点ではまだSvelteで出力したアプリの中身を入れるためのルートフォルダを作っていませんが、ルートフォルダの名前をbrowserとします。このフォルダの使い方は後述します。

ローカルサーバーを起動してルートパス(
/)にだけアクセスすると、アプリのルートフォルダにあるindex.htmlを直接ファイル送信させています。

ただし、index.htmlを読み込んだ後に、さらに必要なファイルを読み込む場合がありますので、
/*.*や、/assets/*.*のリクエストにも、静的ファイルの保存先をexpress.staticを使って教えてあげることも忘れないようにしましょう。

ちなみに標準出力させる文字をエスケープシークエンス(
\x1b[...)で修飾しています。こういうCLIツールを作る際にはエスケープシークエンスもやっていて楽しいので、興味があれば以下のブログ記事もご覧ください。

合同会社タコスキングダム|蛸壺の技術ブログ
【シェルスクリプト実践講座】ANSIエスケープシークエンスを使おう①〜基本的な使い方

シェルアプリを作成する上で欠かせない「ANSIエスケープシークエンス」のテクニックを整理していきます。

pkgバイナリアプリをビルド

次にpkgでのバイナリビルドを行います。

コマンドオプションにビルド設定をすべて指定していくのは結構しんどいので、package.jsonにpkgタグを作成し、ビルドオプションをそこに記述します。

            
            {
    "name": "pkg-express",
    "version": "0.0.1",
    "description": "To execise to use pkg with Express.js.",
    "bin": "index.js",
    "scripts": {
        "pkg": "pkg",
        "build": "tsc && yarn pkg ."
    },
    "pkg": {
        "targets": ["latest-linux-x64"],
        "outputPath": "dist"
    },
    "devDependencies": {
        "pkg": "^5.8.0",
        "tslib": "^2.4.1",
        "typescript": "^4.6.4",
        "@types/express": "^4.17.15"
    },
    "dependencies": {
        "express": "^4.18.2"
    }
}
        
エンドポイントとなるjsファイルは"bin"タグに指定しているか確認し、ビルドターゲットとバイナリの出力先を与えておきます。

ではいよいよビルドの時間です。

            
            $ yarn build
        
無事にビルドできたらdistフォルダ以下にpkgバイナリアプリが出力されているはずです。

            
            $ tree dist
dist/
└── pkg-express
        
ちなみにバイナリのサイズを確認すると、

            
            $ du -sSk dist/
47060   dist/
        
おおよそ47MB程度になっています。

ここからさらにzipで固めてまとめておくと、アプリ配布にも軽量かつ利便性が高いのではないかと思います。


合同会社タコスキングダム|蛸壺の技術ブログ【効果的学習法レポート】nodejsをこれから学びたい人のためのオススメ書籍&教材特集

DebianOS(Linux)でpkgバイナリアプリの起動確認

では最後にSvelteで作ったSPA(index.html側)と、pkg/Express.jsで作ったバイナリアプリを統合して、起動するか試してみましょう。

シェルスクリプトなどで2つのプロジェクトのビルド生成物を自動でまとめてくれるようにすれば楽なのですが、ここでは手動でまとめてみます。

まずは、バイナリプログラム・
pkg-expressのあるフォルダの同階層にbrowserという名前のフォルダを作ります。

            
            $ mkdir browser
$ ls
browser  pkg-express
        
このbrowserフォルダに、Svelteでビルドして生成されたdistの中身のファイルをコピーしましょう。

フォルダ構造の一例を挙げると、

            
            $ tree
.
├── browser
│   ├── assets
│   │   ├── index.501bb487.css
│   │   ├── index.a7c6f463.js
│   │   ├── hoge.a93068e7.svg
│   │   └── piyo.f6732df8.svg
│   ├── favicon.png
│   └── index.html
└── pkg-express
        
のようになっていると思います。

なお、静的なリソース用に
assetsという名前で下位のフォルダを作っていましたが、別の名前のフォルダが存在している場合には、Express.jsでexpress.static(<アセットフォルダまでのパス>)という形で登録しておく必要があります。

ということで、ようやく起動準備まで出来上がりました。

あとは実際に動かしてみましょう。

(※動作中のSPAアプリは適当に作った自作シューティングのデモを動かしています。デフォルトのViteのものではないのでご注意ください。)

これでネットが繋がっていなくてもスタンドアロンなHTMLゲーム等の実行環境が出来上がりました。


合同会社タコスキングダム|蛸壺の技術ブログ【効果的学習法レポート】nodejsをこれから学びたい人のためのオススメ書籍&教材特集

まとめ

ネットワーク環境がなくてもブラウザで動作するタイプのバイナリアプリの作り方を取り上げてみました。

最近のブラウザはかなり高性能なAPIが充実しており、アイデア次第ではかなり有用な使いみちが可能ではないかと思います。

今後また面白い使い道があれば、このブログにて紹介していこうかと思います。
記事を書いた人

記事の担当:taconocat

ナンデモ系エンジニア

主にAngularでフロントエンド開発することが多いです。 開発環境はLinuxメインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。

合同会社タコスキングダム|蛸壺の技術ブログ【効果的学習法レポート】nodejsをこれから学びたい人のためのオススメ書籍&教材特集