Deno で NPM を利用する
takeharak
Deno v1.28 から --unstable なしで npm が利用可能になったが、安定してきたみたいなので試してみる
今回の環境
TL;DR
1. 環境構築
arm64 リリースがないので Apple Silicon / AWS Graviton でも利用できるイメージを用意する
// .devcontainer/devcontainer.json
+ // For format details, see https://aka.ms/devcontainer.json. For config options, see the
+ // README at: https://github.com/devcontainers/templates/tree/main/src/debian
+ {
+ "name": "Deno",
+ "build": {
+ "dockerfile": "Dockerfile"
+ },
+
+ "remoteEnv": {
+ "EDITOR": "code --wait"
+ },
+
+ // Configure tool-specific properties.
+ "customizations": {
+ // Configure properties specific to VS Code.
+ "vscode": {
+ // Set *default* container specific settings.json values on container create.
+ "settings": {
+ // Enables the project as a Deno project
+ "deno.enable": true,
+ // Enables Deno linting for the project
+ "deno.lint": true,
+ // Sets Deno as the default formatter for the project
+ "[typescript]": {
+ "editor.defaultFormatter": "denoland.vscode-deno"
+ }
+ },
+
+ // Add the IDs of extensions you want installed when the container is created.
+ "extensions": [
+ "denoland.vscode-deno"
+ ]
+ }
+ }
+
+ // Use 'forwardPorts' to make a list of ports inside the container available locally.
+ // "forwardPorts": [],
+
+ // Use 'postCreateCommand' to run commands after the container is created.
+ // "postCreateCommand": "deno cache main.ts"
+
+ // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
+ // "remoteUser": "root"
+ }
+
// .devcontainer/Dockerfile
+ ARG VARIANT=bullseye
+ ARG DENO_VERSION=1.33.2
+
+ FROM buildpack-deps:${VARIANT}-curl AS builder
+
+ RUN export DEBIAN_FRONTEND=noninteractive \
+ && apt-get update \
+ && apt-get install -y build-essential \
+ && rm -rf /var/lib/apt/lists/*
+
+ ARG DENO_VERSION
+ ENV DENO_VERSION=${DENO_VERSION}
+
+ RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \
+ && . ${HOME}/.cargo/env \
+ && cargo install --vers ${DENO_VERSION} --root /usr --locked deno
+
+ FROM mcr.microsoft.com/devcontainers/base:${VARIANT}
+
+ ARG USERNAME=vscode
+
+ RUN mkdir -p /deno-dir/ \
+ && chown ${USERNAME}:$(id -gn $USERNAME) /deno-dir/
+
+ ENV DENO_DIR /deno-dir/
+ ENV DENO_INSTALL_ROOT /usr/local
+
+ COPY --from=builder /usr/bin/deno /usr/bin/deno
+
2.新しいプロジェクトを作成
deno init
✅ Project initialized
Run these commands to get started
# Run the program
deno run main.ts
# Run the program and watch for file changes
deno task dev
# Run the tests
deno test
# Run the benchmarks
deno bench
// .vscode/launch.json
+ {
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "request": "launch",
+ "name": "Launch Program",
+ "type": "vscode",
+ "program": "${workspaceFolder}/main.ts",
+ "cwd": "${workspaceFolder}",
+ "runtimeExecutable": "/usr/bin/deno",
+ "runtimeArgs": [
+ "run",
+ "--config",
+ "./deno.jsonc",
+ "--inspect-wait",
+ "--allow-all"
+ ],
+ "attachSimplePort": 9229
+ }
+ ]
+ }
+
3.Express.js を追加
// main.ts
- export function add(a: number, b: number): number {
- return a + b;
- }
+ // @deno-types="npm:@types/express@4"
+ import express from "npm:express@4.18.2";
- // Learn more at https://deno.land/manual/examples/module_metadata#concepts
- if (import.meta.main) {
- console.log("Add 2 + 3 =", add(2, 3));
- }
+ const app = express();
+
+ app.use((req, _res, next) => {
+ console.info(`${req.method} request to "${req.url}" by ${req.hostname}`);
+ next();
+ });
+
+ app.get("/", (_req, res) => {
+ res.status(200).send("Hello from Deno and Express!");
+ });
+
+ const port = Number(Deno.env.get("PORT")) || 3000;
+ const server = app.listen(port, () => {
+ console.debug(`Listening on ${server.address().port} ...`);
+ });
+
+ addEventListener("unload", () => {
+ console.debug("closing HTTP server");
+ server.close(() => {
+ console.debug("HTTP server closed");
+ });
+ });
// deno.jsonc
{
"tasks": {
- "dev": "deno run --watch main.ts"
+ "dev": "deno run --allow-read --allow-env --allow-net --watch main.ts",
+ "start": "deno run --allow-read --allow-env --allow-net main.ts"
}
}
deno task dev
Task dev deno run --allow-read --allow-env --allow-net --watch
main.ts
Listening on 3000 ...
curl http://localhost:3000
Hello from Deno and Express!
4.テストを更新
// main_test.ts
- import { assertEquals } from "https://deno.land/std@0.186.0/testing/asserts.ts";
- import { add } from "./main.ts";
+ import {
+ assertEquals,
+ assertStringIncludes,
+ } from "https://deno.land/std@0.182.0/testing/asserts.ts";
- Deno.test(function addTest() {
- assertEquals(add(2, 3), 5);
- });
+ const stagingUrl = `${Deno.env.get("SCHEME") || "http"}://${
+ Deno.env.get("HOST") || "localhost"
+ }:${Deno.env.get("PORT") || 3000}${Deno.env.get("BASE_PATH") || ""}`;
+
+ Deno.test("hello", async () => {
+ const res = await fetch(`${stagingUrl}/`);
+ assertEquals(res.status, 200);
+ const text = await res.text();
+ assertStringIncludes(text, "Hello");
+ });
deno test --allow-all
Check file:///workspaces/deno-project/main_test.ts
hello ... ok (9ms)
5.ベンチマークを更新
// main_bench.ts
- import { add } from "./main.ts";
+ const stagingUrl = `${Deno.env.get("SCHEME") || "http"}://${
+ Deno.env.get("HOST") || "localhost"
+ }:${Deno.env.get("PORT") || 3000}${Deno.env.get("BASE_PATH") || ""}`;
- Deno.bench(function addSmall() {
- add(1, 2);
- });
-
- Deno.bench(function addBig() {
- add(2 ** 32, 2 ** 32);
- });
+ Deno.bench("hello", async () => {
+ await fetch(`${stagingUrl}/`);
+ });
deno bench --allow-all
cpu: unknown
runtime: deno 1.33.2 (aarch64-unknown-linux-gnu)
file:///workspaces/deno/main_bench.ts
benchmark time (avg) (min … max) p75 p99 p995
------------------------------------------------- -----------------------------
hello 543.1 µs/iter(384.5 µs … 7.37 ms) 570.25 µs 1.34 ms 1.56 ms