NimアプリをWebpackでバンドルする
はじめに
プログラミング言語 Nim は通常 C にコンパイルされ,ネイティブな実行可能ファイルを生成します.
実は,Nim は C 以外にも C++ や JavaScript などの他のターゲットにコンパイルできます.
この記事では,Nim を JavaScript の代替(いわゆる AltJS[1]?)として用いて,Web アプリケーションを Nim で作成し,Webpack でバンドルするまでの前準備についてご紹介します.
また,後半では Nim で SPA を制作する Karax を用いた例もご紹介します.
Nim を Webpack でバンドルする利点
Nim を Webpack を用いてバンドルする利点は,JavaScript 側のモジュール解決もバンドルできることにあるでしょう.
Nim にはそもそもモジュールシステムがあるため,Nim のみで開発する場合にはnim js
コマンドでバンドルまでしてくれます.
一方で,JavaScript と連携する場合には事情は異なります.
Nim のモジュールシステムと JavaScript のモジュールシステムとでは仕組みが異なるため,両者が混在するプロジェクトではバンドルが困難といえます.
Webpack を用いることで,こうした問題を解決できるのです.
環境
記事作成時点の各ツールのバージョンは以下の通りです:
- 必須
- yarn: 1.22.19
- Nim: 1.6.6
- Webpack: 5.72.1
- Webpack CLI: 4.9.2
- nim-loader: 0.3.3
- 必須ではないが今回使ったもの
- karax: 1.2.1
- html-webpack-plugin: 5.5.0
- CoffeeScript: 2.7.0
nim-loader に関する不具合報告は GitHub issues にお願いします.
初期設定からバンドルまで
まずは,プロジェクトを格納するディレクトリを作成します.
$ mkdir nim-webpack-sample
$ cd nim-webpack-sample
JavaScript プロジェクトの初期設定をします.
今回は Yarn を用いましたが,npm を使う場合は適宜読み替えましょう.
$ yarn init
Webpack, nim-loader をそれぞれインストールします.
$ yarn add --dev webpack webpack-cli nim-loader
$ yarn add --dev coffeescript # 必要な人のみ追加
Webpack の設定ファイルを作成します.
今回は JavaScript を一切書かないので,エントリファイルに Nim ファイルを指定してしまいましょう.
path = require('path')
nim_rule =
test: /\.nim$/
use: [{
loader: 'nim-loader'
options:
flags: ['-d:debug'] # Nim compiler options here
}]
module.exports =
mode: 'development'
entry: './src/main.nim' # Nim file as an entry point
output:
path: path.resolve(__dirname, 'dist')
filename: 'main.bundle.js'
module:
rules: [nim_rule]
エントリポイントとなる Nim ファイルを,例えば以下のように作成します.
echo "Hello, Webpack 🤝 Nim World!"
今回の設定例では,このファイルを ./src/
配下に main.nim
として保存しておきます.
ここまでで,環境構築は完了しました!
あとは,Webpack でバンドルを行うだけです.
$ yarn run webpack
これで,./dist/main.bundle.js
としてバンドルされた JavaScript ファイルが生成されました🎉🎉🎉
JavaScript をエントリファイルとして Nim を呼び出す
JavaScript をエントリファイルとする場合は,ソースコードの書き方に工夫が必要です.
利用する Nim ソースコードを以下のように作成します.
FFI として module
という名前の JS オブジェクトを作成し[3],module.exports
に追加することで,Nim のプログラムを JavaScript 側でも利用できるようになります.
import jsffi
var module {.importc,nodecl.}: JsObject
func add5(x: int): int = x + 5
module.exports.add5 = add5
JavaScript のソースコードは以下のように作成します.
import { add5 } from './submodule.nim'
console.log(add5(37)) // => 42
Webpack のエントリファイルを ./src/main.js
に変更すれば,あとは同じようにバンドルを行うだけです.
Karax を使ってみる
Karax とは,Nim の SPA(Single Page Application) ライブラリです.
仮想 DOM を Nim のマクロを使って記述するという特徴があります.
Web アプリケーションのより具体的な実践例として,Karax を用いた例をご紹介します.
Karax をまだインストールしていない場合は,Karax をインストールしましょう.
$ nimble install karax
Webpack で HTML を扱うため,html-webpack-plugin をインストールします.
$ yarn add --dev html-webpack-plugin
テンプレートとなる HTML ファイルを作成します.
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Nim + Webpack Sample App</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
Webpack 設定ファイルを編集して,HTML も同時に出力するようにします.
path = require('path')
+HtmlWebpackPlugin = require('html-webpack-plugin')
nim_rule =
test: /\.nim$/
use: [{
loader: 'nim-loader'
options:
flags: ['-d:debug'] # Nim compiler options here
}]
module.exports =
mode: 'development'
entry: './src/main.nim'
output:
path: path.resolve(__dirname, 'dist')
filename: 'main.bundle.js'
module:
rules: [nim_rule]
+
+ plugins: [
+ new HtmlWebpackPlugin({ template: './src/index.html' })
+ ]
Nim ファイルを編集して,Karax を用いた Web アプリケーションを制作してみましょう.
import karax/[karax, karaxdsl, vdom]
from random import randomize, rand
proc createDom: VNode =
result = buildHtml(tdiv):
h1:
text "Nim/Karax + Webpack Sample"
p:
if rand(100) < 50:
text "Hello!"
else:
text "Aloha!"
randomize()
setRenderer createDom, "app"
最後にビルドをします.
$ yarn run webpack
生成された ./dist/index.html
を開くことで,Karax アプリケーションの動作を確認できます.
まとめ
Nim で作られた Web アプリケーションを Webpack でバンドル化する方法についてご紹介しました.
皆様も Web アプリケーションを Nim で作ってみてはいかがでしょうか?
-
気になったのですが,AltJS という呼び方は何が発祥なのでしょうか.Alternatives to JavaScript の略であり,よく CoffeeScript や TypeScript などを説明するときに使われています.しかし,JavaScript にコンパイルできる言語という定義ならば,例えば C も Emscripten によって JavaScript にコンパイルできますし,必要なツール群が揃いさえすれば多くの言語が AltJS と言えてしまう気がします. ↩︎
-
Nim が文法にオフサイドルールを採用しているので,プロジェクトの他のファイルもオフサイドルールを採用した言語で書かれていると,統一感が感じられて個人的に好きです.HTML の代わりに Pug を,CSS の代わりに Sass を使うとさらに統一感を演出できます. ↩︎
-
importc
プラグマを書くことで,コンパイル後もmodule
という変数名で参照するようになります(これがないとmodule_123456789
のようにマングリングされてしまいます).nodecl
プラグマを書くことで,コンパイル後のコードで変数宣言をしなくなります(importc
さえあれば変数宣言はしなくなりますが,互換性のために書いておくことをおすすめします). ↩︎
Discussion