`deno compile`に設定した引数は生成されたバイナリに引数として埋め込まれる

年を跨いでしまいましたがこの記事はDenoアドベントカレンダー2023の記事です。

https://qiita.com/advent-calendar/2023/deno

Denoでコンパイル時に動的に情報を渡したいなと思い、ドキュメントを見ていたところ以下の3行の記載を発見した。

https://docs.deno.com/runtime/manual/tools/compiler#flags

```
Script arguments can be partially embedded.

> deno compile --allow-read --allow-net https://deno.land/std/http/file_server.ts -p 8080
> ./file_server --help
```

説明がこれしかないが、要するにコンパイル時に渡した`-p 8080`は生成されたバイナリに埋め込まれるよということらしい。

試しに`deno compile`に引数を渡したところ、コンパイル時に指定した引数が生成されたバイナリの引数に組み込まれることがわかった。
この挙動はあまり馴染みがなかったので、興味を持っていくつか試してみた。

```ts:args.ts
console.log(Deno.args);
```

```sh
# まずはdeno run
$ deno run args.ts
[]
$ deno run args.ts args_1
["args_1"]
```


```sh
$ deno compile args.ts # 引数なし
Compile file:///home/jlandowner/args.ts to args
$ ./args
[]
$ ./args args_1
["args_1"]
```


```sh
$ deno compile args.ts compile_1 # 引数を1つ指定
Compile file:///home/jlandowner/args.ts to args
$ ./args
["compile_1"] # 第一引数にコンパイル時に指定した引数が組み込まれている
$ ./args args_1
["compile_1", "args_1"] # 実行時に指定した引数はその後に入る
$ ./args args_1 args_2
["compile_1", "args_1", "args_2"]
```

```sh
$ deno compile args.ts compile_1 compile_2 # 引数を2つ指定
Compile file:///home/jlandowner/args.ts to args
$ ./args
["compile_1", "compile_2"] # コンパイル時に複数の引数を指定した場合も同様
$ ./args args_1
["compile_1", "compile_2", "args_1"] # 実行時に指定した引数はその後に入る
$ ./args args_1 args_2
["compile_1", "compile_2", "args_1", "args_2"]
```

`deno compile`に引数を指定すると生成されたバイナリの引数に組み込まれることがわかった。
`deno compile`に引数を指定する想定のスクリプトに対して`deno run`を行うと引数がないと想定外の挙動になるので注意が必要だが、ドキュメントの例のようにDeno標準パッケージ`std/cli`の`parseArgs`と使うことでやりたかったことができた。

```ts:parse_args.ts
import { parseArgs } from "https://deno.land/std@0.211.0/cli/parse_args.ts";
console.log(parseArgs(Deno.args));
```

```sh
$ deno compile parse_args.ts --opt compile_1
Compile file:///home/jlandowner/parse_args.ts to parse_args

$ ./parse_args
{ _: [], opt: "compile_1" }

$ ./parse_args --opt arg_1
{ _: [], opt: "arg_1" }
```

Deno標準パッケージ`std/cli`の`parseArgs`は[minimist](https://github.com/minimistjs/minimist)の仕様をベースとしているため、同じオプションが指定された際は後に指定された方が優先される。

ちなみに`parseArgs`自体にもデフォルト値を設定する機能や他のスキーマライブラリ等で設定できるので、自分のスクリプトに対して単にデフォルト値を設定するだけでは上記の方法を使うメリットは特にない。

そうではなく、サードパーティのツールに対して自分の環境特有の独自のデフォルト値を埋め込めたり、コミットしたくない情報など埋め込んだ独自のバイナリを作っておく場合に使える（これがやりたかった）

> NOTE: Deno標準パッケージ`std/cli`の`parseArgs`は同じ名前の`node:util`の`parseArgs`とは仕様が異なるため注意が必要。
> - `std/cli`の`parseArgs`仕様: https://github.com/minimistjs/minimist
> - `node:util`の`parseArgs`仕様: https://nodejs.org/api/util.html#utilparseargsconfig

# `deno install`でも

同じような挙動になることがわかった。

```sh
$ deno install args.ts 
✅ Successfully installed args
/home/jlandowner/.deno/bin/args

$ cat /home/jlandowner/.deno/bin/args
#!/bin/sh
# generated by deno install
exec deno run --no-config 'file:///home/jlandowner/args.ts' "$@"
```

`deno install`の場合、単に生成されるシェルに対してinstall時に指定した引数が直接埋め込まれていることが確認できた。

```sh
$ deno install args.ts compile_1 # 引数を1つ指定
✅ Successfully installed args
/home/jlandowner/.deno/bin/args

$ cat /home/jlandowner/.deno/bin/args
#!/bin/sh
# generated by deno install
exec deno run --no-config 'file:///home/jlandowner/args.ts' compile_1 "$@"
```

`deno compile`でも恐らく生成されたバイナリの内部では`deno install`と同じような形でコンパイルがされているのかなと推測している。