とにかくシンプルな静的 HTTP (Web) サーバを Deno で書く

## 背景

ESM (ECMAScript Module) の `import` 宣言を使用している場合、ローカルファイル読み込み (HTML ファイルを直接開く) では動いてくれません。何らかの HTTP サーバを立てて、それ経由で読み込む必要があります。

:::note warn
もうちょっと具体的に言うと: CORS ポリシーに引っかかります。
:::

HTTP サーバといえば Apache とか nginx とかがありますが、しかし簡単なテスト動作をしたいだけなのにルート権限で色々いじくったりするのは面倒だし行儀が悪い。

かといって、わざわざ Docker とか仮想環境を作ってやるほどの規模のことでもない。

ルート権限無しでサッと起動できる HTTP サーバがあると便利なわけです。ということで、Deno で極限シンプルな HTTP サーバを作ってみることにします。

## 準備とか

ファイル構成はこんな感じ。

```txt
./
 |- main.js
 |- static
 |   |- any_script.js
 |   |- any_style.css
 |
 |- pages
     |- index.html
     |- hello.html
```

<details><summary>ファイル内容例</summary>

```any_script.js
window.addEventListener("load", () => {
  console.log("window loaded");
});
```

```any_style.css
@charset "UTF-8";

h1 {
  color: red;
}
```

```index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="/static/any_style.css">
    <script type="module" src="/static/any_script.js"></script>
  </head>
  <body>
    <h1>index</h1>
    <p><a href="./hello.html">hello</a></p>
  </body>
</html>
```

```hello.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="/static/any_style.css">
    <script type="module" src="/static/any_script.js"></script>
  </head>
  <body>
    <h1>hello</h1>  
  </body>
</html>
```

</details>

そして、こんな感じで起動。

```sh
deno --allow-net --allow-read main.js
```

HTML ページ (pages) の URL はこんな感じ。

- `http://localhost:8000/`
- `http://localhost:8000/hello.html`

CSS や JavaScript (static) の URL はこんな感じ。

- `http://localhost:8000/static/any_style.css`
- `http://localhost:8000/static/any_script.js`

## 完成品

```js
import { serveDir } from "jsr:@std/http@1";

Deno.serve(request => {
  const url = new URL(request.url);

  if(url.pathname.startsWith("/static")) {
    return serveDir(request, { fsRoot: "./static", urlRoot: "static"});
  }

  return serveDir(request, { fsRoot: "./pages", urlRoot: ""});
});
```

std パッケージ `std/http` の `serveDir` と、あとは Deno ネームスペース API の `Deno.serve` だけで全て完結します。

:::note
URL がルート `https://localhost:8000/` のときは、自動で `https://localhost:8000/index.html` につなげてくれました。
:::

:::note warn
`serve` や `serveDir` を `https://deno.land/...` から import しているコードを見かけるかもしれません。これは少し古いやり方で、最近では "jsr:..." というのを使うのが公式で推奨されています。

[std ライブラリリファレンス上部にその旨示されています。](https://deno.land/std@0.224.0)

古いとはいっても、そう何年と昔でない ([ブログ](https://deno.com/blog/std-on-jsr)作成日は 2024年5月) のですが。
:::

## pages / static 分けすらしないなら

つまり、こういうファイル構成。

```txt
./
 |- main.js
 |- items
     |- any_script.js
     |- any_style.css
     |- index.html
     |- hello.html
```

<details><summary>ファイル内容例</summary>

```any_script.js
window.addEventListener("load", () => {
  console.log("window loaded");
});
```

```any_style.css
@charset "UTF-8";

h1 {
  color: red;
}
```

`index.html`

```diff_html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
-   <link rel="stylesheet" href="/static/any_style.css">
-   <script type="module" src="/static/any_script.js"></script>
+   <link rel="stylesheet" href="./any_style.css">
+   <script type="module" src="./any_script.js"></script>
  </head>
  <body>
    <h1>index</h1>
    <p><a href="./hello.html">hello</a></p>
  </body>
</html>
```

`hello.html`

```diff_html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
-   <link rel="stylesheet" href="/static/any_style.css">
-   <script type="module" src="/static/any_script.js"></script>
+   <link rel="stylesheet" href="./any_style.css">
+   <script type="module" src="./any_script.js"></script>
  </head>
  <body>
    <h1>hello</h1>  
  </body>
</html>
```

</details>

apache デフォルト設定の `/var/www/html` と同等。

ディレクトリ名変更以外、上の `main.js` をさらに削るだけ。

```main.js
import { serveDir } from "jsr:@std/http@1";

Deno.serve(request => {
  return serveDir(request, { fsRoot: "./items", urlRoot: ""});
});
```

というかこれ、(`import` 除いて) ワンライナーにできてしまう。

```main.js
import { serveDir } from "jsr:@std/http@1";

Deno.serve(request => serveDir(request, { fsRoot: "./items", urlRoot: ""}));
```

## おわり

細かい設定も無し。ステキ。