Open11

TauriでDeno Freshが動くか試す

babiebabie

Deno Freshプロジェクト作成

$ deno run -A -r https://fresh.deno.dev tauri-deno-fresh

Devサーバーを起動して挙動を確認しておく

$ deno task start
babiebabie

Tauriインストール&セットアップ

$ cargo install tauri-cli
...
Installed package `tauri-cli v1.4.0` (executable `cargo-tauri`)

$ cd tauri-deno-fresh
$ cargo tauri init
✔ What is your app name? · Tauri with Deno Fresh
✔ What should the window title be? · Tauri with Deno Fresh
✔ Where are your web assets (HTML/CSS/JS) located, relative to the "<current dir>/src-tauri/tauri.conf.json" file that will be created? · ../build
✔ What is the url of your dev server? · http://localhost:8000
✔ What is your frontend dev command? · deno task start
✔ What is your frontend build command? · deno task build
babiebabie

以下を参考にファイルを編集・作成する。
https://qiita.com/Tsukina_7mochi/items/cdead6cd746384cd91f2#web-ビルド最小構成

buildタスクとesbuildのインポートを追加。

deno.json
{
  "lock": false,
  "tasks": {
    "start": "deno run -A --watch=static/,routes/ dev.ts",
    "build": "deno run -A build.ts",
    "update": "deno run -A -r https://fresh.deno.dev/update ."
  },
  "imports": {
    "$fresh/": "https://deno.land/x/fresh@1.2.0/",
    "preact": "https://esm.sh/preact@10.15.1",
    "preact/": "https://esm.sh/preact@10.15.1/",
    "preact-render-to-string": "https://esm.sh/*preact-render-to-string@6.1.0",
    "@preact/signals": "https://esm.sh/*@preact/signals@1.1.3",
    "@preact/signals-core": "https://esm.sh/*@preact/signals-core@1.2.3",
    "$std/": "https://deno.land/std@0.190.0/",
    "esbuild": "https://deno.land/x/esbuild@v0.18.7/mod.js"
  },
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "preact"
  }
}

ビルドファイルを作成。

build.ts
import * as esbuild from "esbuild";
import { posix } from "$std/path/mod.ts";

const options: esbuild.BuildOptions = {
  entryPoints: [posix.resolve("main.ts")],
  bundle: true,
  outdir: posix.resolve("build"),
  minify: true,
  sourcemap: "external",
};

await esbuild.build(options);

esbuild.stop();
babiebabie

esbuildでビルドしてみる。

$ deno task build
Task build deno run -A build.ts

✘ [ERROR] Top-level await is currently not supported with the "iife" output format

    main.ts:12:0:
      12 │ await start(manifest);
         ╵ ~~~~~

✘ [ERROR] Could not resolve "$std/dotenv/load.ts"

    main.ts:7:7:
      7import "$std/dotenv/load.ts";
        ╵        ~~~~~~~~~~~~~~~~~~~~~

  You can mark the path "$std/dotenv/load.ts" as external to exclude it from the bundle, which will
  remove this error.

✘ [ERROR] Could not resolve "$fresh/server.ts"

    main.ts:9:22:
      9import { start } from "$fresh/server.ts";
        ╵                       ~~~~~~~~~~~~~~~~~~

  You can mark the path "$fresh/server.ts" as external to exclude it from the bundle, which will
  remove this error.

▲ [WARNING] "import.meta" is not available with the "iife" output format and will be empty [empty-import-meta]

    fresh.gen.ts:19:11:
      19 │   baseUrl: import.meta.url,
         ╵            ~~~~~~~~~~~

  You need to set the output format to "esm" for "import.meta" to work correctly.

✘ [ERROR] Could not resolve "$fresh/runtime.ts"

    routes/index.tsx:1:21:
      1import { Head } from "$fresh/runtime.ts";
        ╵                      ~~~~~~~~~~~~~~~~~~~

  You can mark the path "$fresh/runtime.ts" as external to exclude it from the bundle, which will
  remove this error.

✘ [ERROR] Could not resolve "@preact/signals"

    routes/index.tsx:2:26:
      2import { useSignal } from "@preact/signals";
        ╵                           ~~~~~~~~~~~~~~~~~

  You can mark the path "@preact/signals" as external to exclude it from the bundle, which will
  remove this error.

✘ [ERROR] Could not resolve "$fresh/runtime.ts"

    components/Button.tsx:2:27:
      2import { IS_BROWSER } from "$fresh/runtime.ts";
        ╵                            ~~~~~~~~~~~~~~~~~~~

  You can mark the path "$fresh/runtime.ts" as external to exclude it from the bundle, which will
  remove this error.

error: Uncaught (in promise) Error: Build failed with 6 errors:
components/Button.tsx:2:27: ERROR: Could not resolve "$fresh/runtime.ts"
main.ts:7:7: ERROR: Could not resolve "$std/dotenv/load.ts"
main.ts:9:22: ERROR: Could not resolve "$fresh/server.ts"
main.ts:12:0: ERROR: Top-level await is currently not supported with the "iife" output format
routes/index.tsx:1:21: ERROR: Could not resolve "$fresh/runtime.ts"
...
  let error = new Error(`${text}${summary}`);
              ^
    at failureErrorWithLog (https://deno.land/x/esbuild@v0.18.7/mod.js:1615:15)
    at https://deno.land/x/esbuild@v0.18.7/mod.js:1027:25
    at https://deno.land/x/esbuild@v0.18.7/mod.js:972:52
    at buildResponseToResult (https://deno.land/x/esbuild@v0.18.7/mod.js:1025:7)
    at https://deno.land/x/esbuild@v0.18.7/mod.js:1054:16
    at responseCallbacks.<computed> (https://deno.land/x/esbuild@v0.18.7/mod.js:676:9)
    at handleIncomingPacket (https://deno.land/x/esbuild@v0.18.7/mod.js:731:9)
    at readFromStdout (https://deno.land/x/esbuild@v0.18.7/mod.js:652:7)
    at https://deno.land/x/esbuild@v0.18.7/mod.js:1901:11
    at eventLoopTick (ext:core/01_core.js:183:11)

いっぱい出ますねえ。1つ1つ潰していくぞ。

babiebabie

まずこれら:

[ERROR] Top-level await is currently not supported with the "iife" output format

    main.ts:12:0:
      12 │ await start(manifest);
         ╵ ~~~~~
[WARNING] "import.meta" is not available with the "iife" output format and will be empty [empty-import-meta]

    fresh.gen.ts:19:11:
      19 │   baseUrl: import.meta.url,
         ╵            ~~~~~~~~~~~

  You need to set the output format to "esm" for "import.meta" to work correctly.

どっちも、アウトプット・フォーマットがiifeだとダメらしい。

esbuildのイシュー - support for top level await--format=esmでってある。Denoはesmだし丁度いいね。

build.ts
 const options: esbuild.BuildOptions = {
   entryPoints: [posix.resolve("main.ts")],
   bundle: true,
+  format: "esm",
   outdir: posix.resolve("build"),
   minify: true,
   sourcemap: "external",
 };

deno task buildを再実行したらこれらのエラー消えた。

babiebabie

これら全部同じ理由っぽいな。

[ERROR] Could not resolve "$std/dotenv/load.ts"

    main.ts:7:7:
      7import "$std/dotenv/load.ts";
        ╵        ~~~~~~~~~~~~~~~~~~~~~

  You can mark the path "$std/dotenv/load.ts" as external to exclude it from the bundle, which will
  remove this error.

✘ [ERROR] Could not resolve "$fresh/server.ts"

    main.ts:9:22:
      9import { start } from "$fresh/server.ts";
        ╵                       ~~~~~~~~~~~~~~~~~~

  You can mark the path "$fresh/server.ts" as external to exclude it from the bundle, which will
  remove this error.

✘ [ERROR] Could not resolve "$fresh/runtime.ts"

    components/Button.tsx:2:27:
      2import { IS_BROWSER } from "$fresh/runtime.ts";
        ╵                            ~~~~~~~~~~~~~~~~~~~

  You can mark the path "$fresh/runtime.ts" as external to exclude it from the bundle, which will
  remove this error.

✘ [ERROR] Could not resolve "$fresh/runtime.ts"

    routes/index.tsx:1:21:
      1import { Head } from "$fresh/runtime.ts";
        ╵                      ~~~~~~~~~~~~~~~~~~~

  You can mark the path "$fresh/runtime.ts" as external to exclude it from the bundle, which will
  remove this error.

✘ [ERROR] Could not resolve "@preact/signals"

    routes/index.tsx:2:26:
      2import { useSignal } from "@preact/signals";
        ╵                           ~~~~~~~~~~~~~~~~~

  You can mark the path "@preact/signals" as external to exclude it from the bundle, which will
  remove this error.

あ〜、$@で始まるspecifierのImportMapが解決されてませんなぁ……

babiebabie

esbuild_deno_loaderっていうesbuildプラグインがあって、モジュールを解決してくれるっぽい。
https://deno.land/x/esbuild_deno_loader@0.8.1

imports書いて、

deno.json
{
  ...
  "imports": {
    ...
    "esbuild": "https://deno.land/x/esbuild@v0.18.7/mod.js",
    "esbuild-deno-loader": "https://deno.land/x/esbuild_deno_loader@0.8.1/mod.ts"
  },
  ...
}

プラグイン追加して、

build.ts
...
import { denoPlugins } from "esbuild-deno-loader";
...
const options: esbuild.BuildOptions = {
  ...
  plugins: [
    ...denoPlugins(),
  ],
};
...

実行!

$ deno task build
Task build deno run -A build.ts

✘ [ERROR] Module not found "file:///Users/babie/src/github.com/babie/tauri-deno-fresh/$std/dotenv/load.ts". [plugin deno-loader]

    main.ts:7:7:
      7import "$std/dotenv/load.ts";
        ╵        ~~~~~~~~~~~~~~~~~~~~~

✘ [ERROR] Module not found "file:///Users/babie/src/github.com/babie/tauri-deno-fresh/$fresh/server.ts". [plugin deno-loader]

    main.ts:9:22:
      9import { start } from "$fresh/server.ts";
        ╵                       ~~~~~~~~~~~~~~~~~~

✘ [ERROR] Module not found "file:///Users/babie/src/github.com/babie/tauri-deno-fresh/routes/$fresh/runtime.ts". [plugin deno-loader]

    routes/index.tsx:1:21:
      1import { Head } from "$fresh/runtime.ts";
        ╵                      ~~~~~~~~~~~~~~~~~~~

✘ [ERROR] Module not found "file:///Users/babie/src/github.com/babie/tauri-deno-fresh/components/$fresh/runtime.ts". [plugin deno-loader]

    components/Button.tsx:2:27:
      2import { IS_BROWSER } from "$fresh/runtime.ts";
        ╵                            ~~~~~~~~~~~~~~~~~~~

✘ [ERROR] Module not found "file:///Users/babie/src/github.com/babie/tauri-deno-fresh/routes/@preact/signals". [plugin deno-loader]

    routes/index.tsx:2:26:
      2import { useSignal } from "@preact/signals";
        ╵                           ~~~~~~~~~~~~~~~~~

ファーw w w w w
一緒やんけ

babiebabie

ソースコード見たら違った。esbuild_deno_loaderのバグだ。

esbuildonLoad()onResolve()で渡してくるargs.namespaceがデフォルトでfileなのをそのまま使ってるから起こるっぽいね。

https://esbuild.github.io/plugins/#on-resolve-arguments

  • namespace
    This is the namespace of the module containing this import to be resolved, as set by the on-load callback that loaded this file. This defaults to the file namespace for modules loaded with esbuild's default behavior. You can read more about namespaces here.

この辺もイシューに追記しときましたわーん

babiebabie

なんか音沙汰あるかな?と見に行ったら、音沙汰はないけどREADMEの"Using with other plugins"にこの件書いてあった:

https://github.com/lucacasonato/esbuild_deno_loader#using-with-other-plugins

denoPluginsじゃなくてdenoResolverPlugindenoLoaderPluginに分けてimportして、denoResolverPluginに適切なオプションを与えて変換しろということみたい。

気づかなくて悪いことした……手元で動いたらイシュー閉じる。