背景
ESM (ECMAScript Module) の import
宣言を使用している場合、ローカルファイル読み込み (HTML ファイルを直接開く) では動いてくれません。何らかの HTTP サーバを立てて、それ経由で読み込む必要があります。
もうちょっと具体的に言うと: CORS ポリシーに引っかかります。
HTTP サーバといえば Apache とか nginx とかがありますが、しかし簡単なテスト動作をしたいだけなのにルート権限で色々いじくったりするのは面倒だし行儀が悪い。
かといって、わざわざ Docker とか仮想環境を作ってやるほどの規模のことでもない。
ルート権限無しでサッと起動できる HTTP サーバがあると便利なわけです。ということで、Deno で極限シンプルな HTTP サーバを作ってみることにします。
準備とか
ファイル構成はこんな感じ。
./
|- main.js
|- static
| |- any_script.js
| |- any_style.css
|
|- pages
|- index.html
|- hello.html
ファイル内容例
window.addEventListener("load", () => {
console.log("window loaded");
});
@charset "UTF-8";
h1 {
color: red;
}
<!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>
<!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>
そして、こんな感じで起動。
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
完成品
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
だけで全て完結します。
URL がルート https://localhost:8000/
のときは、自動で https://localhost:8000/index.html
につなげてくれました。
serve
や serveDir
を https://deno.land/...
から import しているコードを見かけるかもしれません。これは少し古いやり方で、最近では "jsr:..." というのを使うのが公式で推奨されています。
古いとはいっても、そう何年と昔でない (ブログ作成日は 2024年5月) のですが。
pages / static 分けすらしないなら
つまり、こういうファイル構成。
./
|- main.js
|- items
|- any_script.js
|- any_style.css
|- index.html
|- hello.html
ファイル内容例
window.addEventListener("load", () => {
console.log("window loaded");
});
@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>
+ <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
<!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>
apache デフォルト設定の /var/www/html
と同等。
ディレクトリ名変更以外、上の main.js
をさらに削るだけ。
import { serveDir } from "jsr:@std/http@1";
Deno.serve(request => {
return serveDir(request, { fsRoot: "./items", urlRoot: ""});
});
というかこれ、(import
除いて) ワンライナーにできてしまう。
import { serveDir } from "jsr:@std/http@1";
Deno.serve(request => serveDir(request, { fsRoot: "./items", urlRoot: ""}));
おわり
細かい設定も無し。ステキ。