Denoからnpmパッケージを使用するノウハウ

6 min read読了の目安(約5900字 1

はじめに

DenoはJavaScript/TypeScriptランタイムであるため、既存のNode.jsやブラウザの資産をある程度活かすことができます。

この記事では、Denoからnpmパッケージを使用する際のノウハウについて、パッケージの種別ごとに解説します。

現時点では大部分のnpmパッケージはDenoでの使用を想定しているわけではありません。

そのため、ここで紹介した手法を使用したとしても、必ずしも動作するとは限らないということを前置きしておきます。

DenoとNode.jsの両方をサポートするパッケージ

npmパッケージの中にはDenoとNode.jsの両方をサポートしているパッケージがいくつか存在します。

例)

こういったパッケージを使用する際は、公式ドキュメントに使用方法が記載されている可能性が高いため、それに従うことを推奨します。

例として、Denoからkyパッケージを使用する方法を紹介します:

import ky from "https://unpkg.com/ky@0.24.0/index.js";

const res = await ky.get("https://qiita.com/api/v2/users?page=1");
console.log(await res.json());

Node.jsとブラウザの両方で動作するパッケージ

ブラウザとNode.jsのどちらの環境でも動作するモジュールはDenoでも動作する可能性が高いです。

このようなパッケージを使用する際は、Skypackまたはesm.shからimportすることをおすすめします(※理由については後述)

例として、fast-xml-parserパッケージを使用する方法を紹介します:

import parser from "https://cdn.skypack.dev/fast-xml-parser@3.17.6?dts";

const result = parser.parse(`<root>
  <user>
    <id>1</id>
    <name>hoge</name>
  </user>
</root>`);

console.log(result);

CommonJS形式で配布されているパッケージ

CommonJS形式で配布されており かつ Node.jsのAPIに依存しないパッケージについては、jspm.devからimportすると動作する可能性があります。

jspm.devから配信されるパッケージは、RollupによってES Modules形式へ変換されるためです。

例として、typescriptパッケージを使用する方法を紹介します(※@deno-typesについては後述します):

// @deno-types="https://unpkg.com/typescript@4.0.3/lib/typescript.d.ts"
import { default as ts } from "https://jspm.dev/typescript@4.0.3/lib/typescript.js";

const source = `
const a = 100;
const b = 200;
console.log(a + b);
`;

console.log(ts.transpileModule(source, {
  compilerOptions: {
    removeComments: true,
  }
}));

Node.jsのAPIに依存するパッケージ

Node.jsのAPI(fs, events等)に依存するパッケージについては、esm.shからimportすると動作する可能性があります(※理由については後述)

例として、fast-csvパッケージ(streamパッケージに依存)を使用する方法を紹介します:

import { parse } from "https://esm.sh/@fast-csv/parse@4.3.6?no-check";
import "https://deno.land/std@0.83.0/node/global.ts";

const stream = parse({ headers: true })
  .on("error", (err: Error) => console.error(err))
  .on("data", (row: Record<string, unknown>) => console.log(row))
  .on("end", () => console.log());

stream.write("id,name,age\n");
stream.write("1,hoge,20\n");
stream.write("2,piyo,30\n");
stream.write("3,fuga,40\n");
stream.end();

(補足) JavaScriptで書かれたnpmパッケージに型チェックや自動補完を適用する方法

DenoにはJavaScriptで書かれたパッケージに対して、TypeScriptによる型チェックや自動補完を適用するための方法がいくつか存在します。

例)

  • X-TypeScript-TypesヘッダをサポートするCDNを利用する。
  • @deno-typesを使用する。

個人的な意見としては、以下の方針に従うのがよいのではないかと考えます:

  • 基本的には、X-TypeScript-TypesをサポートするCDN(Skypackまたはesm.sh)からモジュールをimportする。
  • X-TypeScript-TypesをサポートしないCDN(jspm.devUNPKG等)からモジュールをimportする際は、@deno-typesを使用する。

X-TypeScript-Typesとは?

まず、Denoでリモートモジュールをimportする際の挙動について簡単に解説します。

リモートモジュールとは、以下のようなものを指します:

import * as fs from "https://deno.land/std@0.83.0/fs/mod.ts";
import React from "https://esm.sh/react@16.14.0";
import faker from "https://cdn.skypack.dev/faker@5.1.0?dts";
  1. importされるモジュールがリモートモジュールであるか確認する。
  2. リモートモジュールであれば、それがローカルにキャッシュされているか確認する。
  3. キャッシュされていなければ、HTTP/HTTPS経由でそのモジュールをダウンロードし、ローカルにキャッシュする。

Denoは上記3でリモートモジュールをダウンロードした際に、レスポンスにX-TypeScript-Typesヘッダが設定されているかを確認します。

もしX-TypeScript-Typesヘッダが設定されていれば、そこで指定されたファイルも一緒にダウンロードし、そのリモートモジュールに対する型定義ファイル(*.d.ts)として扱います。

この仕組みによって、JavaScriptで書かれたモジュールに対しても型チェック等を適用できるようになります。

以下にX-TypeScript-TypesヘッダをサポートしているCDNを紹介します:

@deno-typesとは?

X-TypeScript-Typesヘッダ同様、JavaScriptで書かれたモジュールに型チェック等を適用するための仕組みです。

例えば、次のようなJavaScriptモジュールが存在したとします。

add.js
export function add(a, b) {
  return a + b;
}

以下は、このモジュールに対する型定義ファイルです。

add.d.ts
export function(a: number, b: number): number;

add.jsimportする際に、@deno-typesによってadd.d.tsを指定することで、型チェック等が有効化されます。

// @deno-types="./add.d.ts"
import { add } from "./add.js";

console.log(add(1, 2));
console.log(add("2", 3)); // コンパイルエラー!

(補足) CDNの紹介

Skypack

元々はPika CDNと呼ばれていました。

以下のような機能をサポートしています:

  • HTTP/2
  • HTTP/3
  • Brotli
  • X-TypeScript-Typesヘッダ (以下の例のように、URLのクエリとしてdtsを付与すると設定されます)
import faker from "https://cdn.skypack.dev/faker@5.1.0?dts";
	
console.log(faker.name.findName());

esm.sh

OSSとして開発されているCDNです。

以下のような機能をサポートしています:

  • X-TypeScript-Typesヘッダ (URLのクエリにno-checkを付与することで無効化できます)
  • esbuildを使用したパッケージのES Modules形式への変換
  • Node.jsのネイティブモジュールをDenoのNode.js互換レイヤ(std/node)への読み込みに置換する
  • Bundle mode

おわりに

この記事ではDenoからnpmパッケージを使用するノウハウについて書きました!

参考情報