準備
npm create rescript-app@latest
実行結果
> npx
> create-rescript-app
┌ create-rescript-app 1.7.1
│
◇ Welcome to ReScript! ─────────────────────────────────╮
│ │
│ Fast, Simple, Fully Typed JavaScript from the Future │
│ https://rescript-lang.org │
│ │
├────────────────────────────────────────────────────────╯
│
◇ New Project ───────────────────────────────────────────╮
│ │
│ Create a new ReScript 11 project with modern defaults │
│ ("Core" standard library, JSX v4) │
│ │
├─────────────────────────────────────────────────────────╯
│
◇ What is the name of your new ReScript project?
│ viz-test
│
◇ Select a template
│ Basic
│
◇ Versions loaded.
│
◇ ReScript version?
│ 11.1.1
│
◇ ReScript Core version?
│ 1.5.0
│
◇ Project created.
│
└ Happy hacking!
cd viz-test
npm install @viz-js/viz
ReScriptのインストール時に指定したプロジェクト名(今回はviz-test)でディレクトリが作成される。そのディレクトリに移動してからインストールを実行する
<!doctype html>
<html>
<head>
<title>Viz Test</title>
<meta charset="utf8">
</head>
<body>
<script type="module" src="VizTest.res.js"></script>
</body>
</html>
type viz
@module("@viz-js/viz") external instance: () => promise<viz> = "instance"
@send external renderSVGElement: (viz, string) => Dom.element = "renderSVGElement"
@get external body: Dom.document => Dom.element = "body"
@send external appendChild: (Dom.element, Dom.element) => unit = "appendChild"
let str = `digraph {
"見 て い る" -> "望遠鏡 で" [label = implement]
"見 て い る" -> "星 を" [label = object]
"女の子 を" -> "見 て い る" [label = adjectivals]
"見 た" -> "公園 から" [label = source]
"見 た" -> "女の子 を" [label = object]
}`
let viz = await instance()
document->body->appendChild(viz->renderSVGElement(str))
viz-test/
├── rescript.json
├── package.json
└── src/
├── viz_test.html
└── VizTest.res
VizTest.res
でやっていること
instance
のバインド
Viz.js
のinstance
をバインドします。
https://viz-js.com/api/#viz.instance を見るとinstance
の型は() => promise<viz>
です。
external instance: () => promise<viz> = "instance"
JavaScriptのモジュールをバインドするので頭に@module()
を付けます。
@module("@viz-js/viz") external instance: () => promise<viz> = "instance"
renderSVGElement
のバインド
renderSVGElement
の型は https://viz-js.com/api/#viz.Viz.renderSVGElement を見るとstring => SVGSVGElement
ですが、SVGSVGElement
ってなんでしょう。
最後はappendChild
で貼り付けるのでDom.Element
型を返すんだろうと判断しました。
オブジェクトメソッドなので@send
でバインドします。
@send external renderSVGElement: (viz, string) => Dom.element = "renderSVGElement"
DOMのバインド
ReScriptでDOMを扱うを御参照ください。
ビルドして実行
npm run res:build
コンパイルはエラーなく終了します。
ローカルサーバを起動してviz_test.html
を読み込ませ、コンソールを見るとエラーが出ています。
viz_test.html:1 Uncaught TypeError: Failed to resolve module specifier "@viz-js/viz". Relative references must start with either "/", "./", or "../".
npmパッケージが読み込めないようです。
Parcel
でバンドル
npm install --save-dev parcel
npx parcel build src/VizTest.res.js
🚨 Build failed.
@parcel/optimizer-swc: await isn't allowed in non-async function
バンドルに失敗しました。
トップレベルでawait
を使っているのが駄目なんでしょうか。
VizTest.res
の修正
then
を使って書き換えます。
instance()->then(viz => {
document->body->appendChild(viz->renderSVGElement(str))
})
npm run res:build
でコンパイルするとエラーが出ます。
The value then can't be found
then
が見つからないと言っています。居場所を教えます。
instance()->Promise.then(viz => {
document->body->appendChild(viz->renderSVGElement(str))
})
コンパイルするとまだエラーが出ます。
This has type: unit
But it's expected to have type:
RescriptCore.Promise.t<'a> (defined as promise<'a>)
Promise.then
の型はlet then: (t<'a>, 'a => t<'b>) => t<'b>
です。1
いま第一引数がpromise<viz>
なので、返り値はpromise<'b>
でないといけません。
instance()->Promise.then(viz => {
document->body->appendChild(viz->renderSVGElement(str))
Promise.resolve()
})
解決済みのPromise
を返すようにしました。
コンパイルするとまたエラーです。
This function call is at the top level and is expected to return `unit`. But it's returning `RescriptCore.Promise.t<unit>`.
トップレベルなのにunit
が返ってないとのことです。
ignore
付けます。
instance()->Promise.then(viz => {
document->body->appendChild(viz->renderSVGElement(str))
Promise.resolve()
})->ignore
これでコンパイルが通ります。
再びParcel
でバンドル
エラーがでたのでキャッシュを消しました。
rm -rf .parcel-cache
バンドルします。
npx parcel build src/VizTest.res.js
今度は成功しました。
viz-test/
├── rescript.json
├── package.json
├── src/
│ ├── viz_test.html
│ └── VizTest.res
└── dist/
├── VizTest.res.js
└── VizTest.res.js.map
viz_test.html
の修正
dist
の下のVizTest.res.js
を読み込むよう修正します。
<script src="../dist/VizTest.res.js"></script>
ブラウザで確認するとエラーなく動いています。
おまけ
node_modules/@viz-js/viz/lib
にあるviz-standalone.mjs
をコピーしてきてsrcに置きます。
viz-test/
├── rescript.json
├── package.json
└── src/
├── viz_test.html
├── VizTest.res
└── viz-standalone.mjs
これで
type viz
@module("./viz-standalone.mjs") external instance: () => promise<viz> = "instance"
@send external renderSVGElement: (viz, string) => Dom.element = "renderSVGElement"
@get external body: Dom.document => Dom.element = "body"
@send external appendChild: (Dom.element, Dom.element) => unit = "appendChild"
let str = `digraph {
"見 て い る" -> "望遠鏡 で" [label = implement]
"見 て い る" -> "星 を" [label = object]
"女の子 を" -> "見 て い る" [label = adjectivals]
"見 た" -> "公園 から" [label = source]
"見 た" -> "女の子 を" [label = object]
}`
let viz = await instance()
document->body->appendChild(viz->renderSVGElement(str))
と、相対パスでインポートすると、なにもかもうまくいきます。
node_modules
からモジュールを引っ張ってくるのが正しいのか分からなかったので、Parcel
でバンドルすることにしました。