Deno入門[npmモジュールの使用]

この記事は筆者の[ソロ Advent Calendar 2022](https://qiita.com/advent-calendar/2022/panda) 16日目の記事です。

Denoの公式ドキュメントの内容を実際に動かしてみてDenoに入門してみたのでその備忘録の続きです。
今回はDenoでnpmモジュールを使用する方法について試してみたいと思います。

[Deno入門[インストール、環境構築、ファイル実行、標準ライブラリ]](https://qiita.com/JY8752/items/9cd6c05394bf955bdd75)
[Deno入門[CLIコマンド、モジュール、環境変数、Webフレームワーク]](https://qiita.com/JY8752/items/a00866917626a8719ebb)
[Deno入門[npmモジュールの使用]](https://qiita.com/JY8752/items/d177e176a7544b693a34) <- 今ここ
[Deno入門[prisma + oakで作るAPI]](https://qiita.com/JY8752/items/198762a69d463ac90fab)
[Deno入門[テスト編]](https://qiita.com/JY8752/items/e7e52aae6a0a3eb69572)

# Denoのnpm対応について
Denoは最初npmの対応はしていなかったようですが、[Bun](https://bun.sh/)のようなnodeよりも速く、npmにも対応したランタイムの登場の影響かはわかりませんがnpm対応がされました。

執筆時点(v1.28.2)でDenoでnpmを使用するにはnpm specifiersまたはCDNを利用することで利用が可能となるようです。

# npm specifiersでnpmモジュールを使用する
Denoのnpm specifiersはまだ完全に安定したものではないためCDNでの利用の方が安定していたようですがDeno 1.28よりnpmのサポートは安定してきたようです。とはいえ、まだnpm specifiersのみでnpmモジュールの利用が不完全な時はCDNを利用する必要があるようです。

Denoでexpressを使用してみる
```typescript:express.ts
import express from "npm:express@^4.18";
const app = express();

app.get("/", function (_: any, res: any) {
  res.send("Hello World");
});

app.listen(3000);
console.log("listening on http://localhost:3000/");
```

```
deno run --allow-read --allow-env --allow-net npm/express.ts
listening on http://localhost:3000/

% curl localhost:3000
Hello World
```

上記のように```npm:<パッケージ名>[@バージョン][/サブパス]```の形式でimportして使用することが可能。

# CDNでnpmモジュールを使用する
CDNはUNPKGのようによく知られたものからいくつか選択肢があり、選べるようだがDenoで利用することを想定したDenoフレンドリーのCDNがあるためそれを使うといいかもしれない。

DenoフレンドリーなCDNには例えばesm.shがあり使用は以下のようになる。

```typescript
import React from "https://esm.sh/react";

export default class A extends React.Component {
  render() {
    return <div></div>
  }
}
```


# DenoでReactを使ってみる
公式ドキュメントに代表的なnpmモジュールの使用ガイドが記載されていますが、今回はせっかくなのでReactをガイドに従って使用してみたいと思います。

## create vite extra
DenoでReactアプリをscaffoldするのにcreate-vite-extraを利用することができる。

```
deno run --allow-env --allow-read --allow-write npm:create-vite-extra
```

reactを選択し起動してみる。
```
deno task dev
```

動いた！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551753/5978585c-27d4-00bd-7e40-a3305388a488.png)


```react-router-dom ```を追加してページ遷移も試してみる。

```diff_typescript:vite.config.mjs
import { defineConfig } from "npm:vite@^3.1.3";
import react from "npm:@vitejs/plugin-react@^2.1";

import "npm:react@^18.2";
import "npm:react-dom/client@^18.2";
+ import "npm:react-router-dom@^6.4"; // Add this line

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
});
```

```javascript:main.jsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import {
  BrowserRouter as Router,
  Route,
  Routes,
} from "react-router-dom";
import App from './App'
import './index.css'
import Hello from './pages/Hello.jsx';

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <Router>
      <Routes>
        <Route exact path="/" element={<App />} />
        <Route exact path="/hello" element={<Hello message="Deno!!" />} />
      </Routes>
    </Router>
  </React.StrictMode>
)
```

```diff_javascript:App.jsx
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import './App.css'

function App() {
  const [count, setCount] = useState(0)

  return (
    <div className="App">
      <img src="/vite-deno.svg" alt="Vite with Deno" />
      <div>
        <a href="https://vitejs.dev" target="_blank">
          <img src="/vite.svg" className="logo" alt="Vite logo" />
        </a>
        <a href="https://reactjs.org" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React</h1>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
        <p>
          Edit <code>src/App.jsx</code> and save to test HMR
        </p>
      </div>
      <p className="read-the-docs">
        Click on the Vite and React logos to learn more
      </p>
+      <a href="/hello">To Hello Deno!!</a>
    </div>
  )
}

export default App
```

追加したリンクをクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551753/21ca9f5d-28c3-50e2-82d7-58f493c97395.png)

わかりづらいですがページ遷移も確認できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551753/78485d1c-954a-bc59-1f8b-4d02ecbef0d7.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551753/47798671-93c5-e5de-7758-73da4d3ad4f9.png)

# まとめ
まだDenoでのnpmモジュールの対応は開発段階のため、実プロジェクトでの採用は難しいかもしれませんが触った感じ普通に使えて、フロントエンドのの開発に慣れていない筆者にとってはかなり快適に開発できました。npmに対応したことでJS周辺のライブラリを利用する時にDenoでの利用が選択できるため個人的に手元で何かやる時は積極的にDenoを使っていきたいなと思います！