20200514のNode.jsに関する記事は9件です。

Node.jsでAPIから取得したJSON(ネストあり)を扱う

メディア芸術データベースのAPIを使用してJSONデータを取得したものの、Node.jsでネストありのJSONデータを扱う方法に少し手間取ったため備忘録。
Node.jsでAPIデータ取得→ネストありのJSONデータの扱いについての記事がなかったため残します。

メディア芸術データベース

文化庁がマンガやアニメ、ゲームといったメディア作品の情報を保存し、ベータ版としてデータベースを公開している。

  • 検索サイト

https://mediaarts-db.bunka.go.jp/

  • データベースについて

https://mediaarts-db.bunka.go.jp/about#anc02

APIで取得したJSONの形式

saenai-heroine-no-sodatekata.json
{"hitnum":1,
"record":[{"fieldId":"animation",
           "categoryId":"an-col",
           "subcategoryId":"an207",
           "aipId":"C15461",
           "metadata":{"rdf:type":["Collection"],
                       "schema:additionalType":["an"],
                       "schema:genre":["テレビレギュラーアニメシリーズ"],
                       "schema:name":["冴えない彼女*の 育てかた[ヒロイン]"],
                       "schema:name xml:lang=\"ja-Hrkt\"":["サエナイヒロインノソダテカタ","サエナイカノジョノソダテカタ"],
                       "schema:creator":["[監督]亀井幹太"],
                       "schema:contributor":["[シリーズ構成]丸戸史明 / [監督]亀井幹太 / [キャラクターテザイン] / [キャラクターテザイン、総作画監督]高瀬智章 / [美術監督]日下部夏月 / [美術設定]綱頭瑛子 / [音響監督]藤田亜紀子 / [音楽]百石元(F.M.F) / [アニメーション制作]A-1 Pictures / [制作]冴えない製作委員会 / [制作]アニプレックス / [制作]フジテレビジョン / [制作]KADOKAWA富士見書房BC / [制作]京楽産業ホールディングス / [制作]関西テレビ放送 / [制作]電通 / [制作]グッドスマイルカンパニー / [制作]コスパ"],
                       "schema:actor":["【安芸倫也】松岡禎丞 / 【澤村・スペンサー・英梨々】大西沙織 / 【霞ヶ丘詩羽】茅野愛衣 / 【加藤恵】安野希世乃 / 【氷堂美智留】矢作紗友里 / 【波島伊織】柿原徹也 / 【波島出海】赤崎*千夏 / 【石巻基予】大地葉 / 【里見梓】奥野香耶 / 【上郷喜彦】内田雄馬 / 【町田苑子】桑島法子 / 【加藤圭一】斉藤壮馬 / 【澤村小百合】中原麻衣 / 【水原叡智佳】田辺留依 / 【姫川時乃】鈴木絵理 / 【森丘藍子】大地葉"],
                       "schema:publisher":["CX"],
                       "schema:datePublished":["2015-01-09"],
                       "schema:startDate":["2015-01-09"],
                       "schema:endDate":["2015-03-27"],
                       "ma:type":["レギュラー"],
                       "schema:isPartOf":["C5951"],
                       "ma:media":["TVレギュラー"],
                       "ma:Effect":["アニメ"],
                       "ma:periodDisplayed":["00時50分 / フジテレビで放映 / 30分枠"],
                       "ma:runtime":["30分"],
                       "ma:volumeNumber":["13"],
                       "ma:originalCreator":["[原作]丸戸史明"],
                       "ma:contributor":["[アニメーション制作]A-1 Pictures / [制作]冴えない製作委員会 / [制作]アニプレックス / [制作]フジテレビジョン / [制作]KADOKAWA富士見書房BC / [制作]京楽産業ホールディングス / [制作]関西テレビ放送 / [制作]電通 / [制作]グッドスマイルカンパニー / [制作]コスパ"],
                       "ma:source":["CRNT2015(カレント2015新規登録)"],
                       "madbdata:oldDateModified":["2016-04-01T11:44:40"]}
                      }
          ]
}

メディア芸術データベースで1件だけヒットさせた結果が上記の通りである。
「"record"」キー配下に検索でヒットした結果のレコードが配列的に格納される。

この記事では、「"record"」キー配下の「"metadata"」の中の「"schema:name"」のValueを扱いたい!みたいな時のやり方の備忘録である。

Node.js(express)のコード(APIで取得→配列に格納→コンソール出力)

index.js
const express = require('express');
const bodyParser = require('body-parser');
const https = require('https');

// define the Express app
const app = express();

//メディア芸術データベースAPIのURL
const mediaartsdbAPI = 'https://mediaarts-db.bunka.go.jp/api/search?aipId=C15461';

// use bodyParser to parse application/json content-type
app.use(bodyParser.json());

// アプリケーションの「http://localhost:4000/」にアクセスすると、メディア芸術データベースへHTTPでGETしにいく。
app.get('/', (req, res) => {
    //GETしたJSON(バイナリ)をテキスト化&JSONパースし格納するための変数。
    let mediaartsdbRec1 = '';
    //「"record"」配下の要素を格納するための配列。
    let mediaartsdbRec2 = [];

    const mediaartsdbGet = https.request(mediaartsdbAPI, (res) => {
        let buf = [];
        res.on('data', (chunk) => {
            buf.push(chunk);
            //bufにはバイナリの配列の形でJSONが格納される。
        });
        res.on('end', () => {
            //バイナリをテキスト化し、それをJSONパースする。
            mediaartsdbRec1 = JSON.parse(buf.toString());

            //「"record"」の要素を配列に格納していく。
            for (j in mediaartsdbRec1['record']){
                mediaartsdbRec2.push(mediaartsdbRec1['record'][j]);
            }
            console.log(mediaartsdbRec2[0]['metadata']['schema:name']);
            console.log('JSONデータは以上です。');
        });
    })
    mediaartsdbGet.on('error', (e) => {
        console.error(`エラーが出ました: ${e.message}`);
      });
    mediaartsdbGet.end();
    res.send(mediaartsdbRec2);
});

// start the server
app.listen(4000, () => {
  console.log('listening on port 4000');
});

http://localhost:4000/にブラウザでアクセスした時のコンソール実行結果

/App/backend # node src
listening on port 4000
[ '冴えない彼女*の 育てかた[ヒロイン]' ]
JSONデータは以上です。

「"record"」キー配下の「"metadata"」の中の「"schema:name"」のValueをconsole.logで表示できました。
あとは同じようにして変数に入れるなりすれば活用できるかと。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

expo-cliのinstallでpermission deniedエラー

前提

npm install expo-cli --globalでexpo-cliを使用できるようにインストールしようとした時のお話

mac

本題

npm install expo-cli --globalで下記のようなpermission deniedのエラー発生。

terminal.
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for @expo/ngrok-bin-freebsd-x64@2.2.8: wanted {"os":"freebsd","arch":"x64"} (current: {"os":"darwin","arch":"x64"})

npm ERR! code EACCES
npm ERR! syscall access
npm ERR! path /usr/local/lib/node_modules
npm ERR! errno -13
npm ERR! Error: EACCES: permission denied, access '/usr/local/lib/node_modules'
npm ERR!  [Error: EACCES: permission denied, access '/usr/local/lib/node_modules'] {
npm ERR!   stack: "Error: EACCES: permission denied, access '/usr/local/lib/node_modules'",
npm ERR!   errno: -13,
npm ERR!   code: 'EACCES',
npm ERR!   syscall: 'access',
npm ERR!   path: '/usr/local/lib/node_modules'
npm ERR! }
npm ERR! 
npm ERR! The operation was rejected by your operating system.
npm ERR! It is likely you do not have the permissions to access this file as the current user
npm ERR! 
npm ERR! If you believe this might be a permissions issue, please double-check the
npm ERR! permissions of the file and its containing directories, or try running
npm ERR! the command again as root/Administrator.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/ユーザー名/.npm/_logs/2020-05-14T12_14_47_487Z-debug.log

解決方法

nmpの公式サイトを見ると解決!

sudo chown -R $(whoami) $(npm config get prefix)/{lib/node_modules,bin,share}

もう一度、npm install expo-cli --global実行!

expo --versionでインストールできているか確認!

expo-cliでできること
JavaScriptのコードをビルドする
本番公開の際にpublishする

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Win10 + node.js + SQL Server 2019 Express で Tedious StoredProcedure を少し試してみる

Win10 + node.js + SQL Server 2019 Express で Tedious StoredProcedure を少し試してみる

目的

tedious/examples/storedProcedureWithParameters.js
を参考に少し試してみる
対象はWin10 + node.js -> Ubuntu 18.04 + SQL Server 2019 Express

対象テーブル

データの並びは以下に準拠
郵便番号データの説明
対象データにユニークキーになりそうなデータが無いので
8桁のテキストを追加する

CREATE TABLE [dbo].[ZIPCODE](
    [SEQ] [nchar](8) NOT NULL,
    [PREFCODE] [nchar](3) NULL,
    [KUBUNCODE] [nchar](8) NULL,
    [POSTAL5] [nchar](5) NULL,
    [POSTAL] [nchar](8) NULL,
    [PREFKANA] [nchar](20) NULL,
    [CITYKANA] [nchar](40) NULL,
    [ADDRKANA] [nchar](80) NULL,
    [PREFKANJI] [nchar](20) NULL,
    [CITYKANJI] [nchar](40) NULL,
    [ADDRKANJI] [nchar](80) NULL,
    [FLG1] [int] NULL,
    [FLG2] [int] NULL,
    [FLG3] [int] NULL,
    [FLG4] [int] NULL,
    [FLG5] [int] NULL,
    [FLG6] [int] NULL
) ON [PRIMARY]

サンプル Stored Procedure

USE DEMO; 
GO 
CREATE PROCEDURE GETZIPCODE
 @SEQ        NCHAR(8),
 @OPREFCODE  NCHAR(3)  OUTPUT,
 @OPOSTAL    NCHAR(8)  OUTPUT,
 @OCITYKANJI NCHAR(40) OUTPUT,
 @OADDRKANJI NCHAR(80) OUTPUT
AS  
SELECT @OPREFCODE  = PREFCODE
     , @OPOSTAL    = POSTAL
     , @OCITYKANJI = CITYKANJI
     , @OADDRKANJI = ADDRKANJI
FROM ZIPCODE 
WHERE SEQ = @SEQ;
GO

動作確認

 DECLARE @OPREFCODE  NCHAR(3)
 DECLARE @OPOSTAL    NCHAR(8)
 DECLARE @OCITYKANJI NCHAR(40)
 DECLARE @OADDRKANJI NCHAR(80)

EXECUTE GETZIPCODE N'00000001'
      , @OPREFCODE  OUTPUT
      , @OPOSTAL    OUTPUT
      , @OCITYKANJI OUTPUT
      , @OADDRKANJI OUTPUT

SELECT  @OPREFCODE, @OPOSTAL, @OCITYKANJI, @OADDRKANJI

サンプルコード

var Connection = require('tedious').Connection; 
var Request = require('tedious').Request;
var TYPES = require('tedious').TYPES;

var connectionConfig = {
    userName: 'demo',
    password: 'demo',
    server: '192.168.5.49',
    options: {
        database: 'demo'
    }
};

var connection = new Connection(connectionConfig);

connection.on('connect', function(err) {  
    if (err) {
        console.log(err);
        console.log('err:connect');
        connection.exit;
        process.exit(0);
    }
    console.log("Connected");
        if (err) {
            console.log(err);
            console.log('err:request');
            process.exit(0);
        }

    execstoredProcedure();
});

function execstoredProcedure() {

    request = new Request("GETZIPCODE", function(err, rowCount) {
        if (err) {
            console.log(err);
            connection.exit;
            process.exit(0);
        }
    });

    request.on('doneInProc', function (rowCount, more, rows) {  
        console.log('doneInProc: '+ rowCount + ' row(s) returned'); 
    });

    request.on('returnValue', function(paramName, value, metadata) {
        console.log('returnValue: ' + paramName + ' : ' + value);
    });

    request.on('requestCompleted', function (err) { 
        console.log("requestCompleted: execstoredProcedure");
        connection.exit;
        process.exit(0);
    });
    // The name should not start '@'.
    request.addParameter('SEQ', TYPES.NVarChar, '00000001');
    request.addOutputParameter('OPREFCODE',  TYPES.NVarChar);
    request.addOutputParameter('OPOSTAL',    TYPES.NVarChar);
    request.addOutputParameter('OCITYKANJI', TYPES.NVarChar);
    request.addOutputParameter('OADDRKANJI', TYPES.NVarChar);

    // Call a stored procedure represented by request.
    connection.callProcedure(request);
};

参考にしたのは以下のサイト

tedious/examples/storedProcedureWithParameters.js
Tedious Request
Package - mssql-tedious-int64
Win10 + node.js から SQL Server 2019 Express に CSVファイルを書き込んでみる
Win10 + node.js から SQL Server 2019 Express に CSVファイルを書き込んでみる 修正版
Windows 10 Pro x64 + Excel2016 x86 VBA + ADODB.Command を使って SQL Server 2016 Express でストアドを試してみる

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.js のバージョン管理ツール nvm for Windowsを導入

導入背景

ローカル環境とデプロイ先の環境でNode.jsのバージョンに差異があり、その管理を行うため。

nvm for Windowsをインストール

nvm-setup.zipをダウンロードし、インストール
公式からダウンロード

nvm for Windows のコマンド

  • インストール可能なバージョンリストを表示
>nvm list available

|   CURRENT    |     LTS      |  OLD STABLE  | OLD UNSTABLE |
|--------------|--------------|--------------|--------------|
|    14.2.0    |   12.16.3    |   0.12.18    |   0.11.16    |
|    14.1.0    |   12.16.2    |   0.12.17    |   0.11.15    |
|    14.0.0    |   12.16.1    |   0.12.16    |   0.11.14    |
|   13.14.0    |   12.16.0    |   0.12.15    |   0.11.13    |
|   13.13.0    |   12.15.0    |   0.12.14    |   0.11.12    |
|   13.12.0    |   12.14.1    |   0.12.13    |   0.11.11    |
|   13.11.0    |   12.14.0    |   0.12.12    |   0.11.10    |
|   13.10.1    |   12.13.1    |   0.12.11    |    0.11.9    |
|   13.10.0    |   12.13.0    |   0.12.10    |    0.11.8    |
|    13.9.0    |   10.20.1    |    0.12.9    |    0.11.7    |
|    13.8.0    |   10.20.0    |    0.12.8    |    0.11.6    |
|    13.7.0    |   10.19.0    |    0.12.7    |    0.11.5    |
|    13.6.0    |   10.18.1    |    0.12.6    |    0.11.4    |
|    13.5.0    |   10.18.0    |    0.12.5    |    0.11.3    |
|    13.4.0    |   10.17.0    |    0.12.4    |    0.11.2    |
|    13.3.0    |   10.16.3    |    0.12.3    |    0.11.1    |
|    13.2.0    |   10.16.2    |    0.12.2    |    0.11.0    |
|    13.1.0    |   10.16.1    |    0.12.1    |    0.9.12    |
|    13.0.1    |   10.16.0    |    0.12.0    |    0.9.11    |
|    13.0.0    |   10.15.3    |   0.10.48    |    0.9.10    |
  • 特定のバージョンの node をインストール
>nvm install 10.20.1
Downloading node.js version 10.20.1 (64-bit)...
Complete
  • インストール済みのバージョンリストを表示
>nvm list
  * 12.16.3 (Currently using 64-bit executable)
    10.20.1
  • 使用するバージョンを指定する
>nvm use 10.20.1
Now using node v10.20.1 (64-bit)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

1分でDenoを触る

Deno v1.0が出たので、とりあえず触りたいという方向けに。

起動

Docker化してくれてる方がいるのでさらっと起動します。

docker run -it --entrypoint "bash" hayd/ubuntu-deno

サンプルを動かす

公式のサンプルを動かします。

deno run https://deno.land/std/examples/welcome.ts
アウトプット
Download https://deno.land/std/examples/welcome.ts
Warning Implicitly using master branch https://deno.land/std/examples/welcome.ts
Compile https://deno.land/std/examples/welcome.ts
Welcome to Deno ?

さらっとTypeScriptが走り恐竜くんが現れましたね。
あとは色々触ってみましょう。

参考

環境

% sw_vers
ProductName:    Mac OS X
ProductVersion: 10.15.4
BuildVersion:   19E287

% docker --version
Docker version 19.03.8, build afacb8b
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Node.js】JavaScriptで組み合わせ,順列を列挙する (npmモジュールあり)

JSで組み合わせと順列を列挙する標準メソッドはない

Rubyだと

index.rb
array = [1,3,5];

#組み合わせ
p array.combination(2).to_a # => [[1, 3], [1, 5], [3, 5]]

#順列
p array.permutation(2).to_a # => [[1, 3], [1, 5], [3, 1], [3, 5], [5, 1], [5, 3]]

こんな感じで簡単に組み合わせ、順列の列挙ができるのにJavaScriptだとこれがない。Mathオブジェクトのメソッドにあってもいい気がするんだけど。。。

とにかくこのままだとpaizaで組み合わせ、順列の問題が出てくるたびにrubyで一から書き直す羽目になるので、JavaScriptで順列と組み合わせを列挙するコードを考え、ついでにrubyのコードより分かりやすく記述できるnpmモジュールも作りました。

コード

これから載せるのは実際にモジュールを作る際に書いたコードなのでしっかり動くはずです。

組み合わせ

//0~n-1の数字をk個ずつの組み合わせで列挙する関数
function rec(n, k) {
    if (k === 0) return [[]];
    const result = [];
    for (let i = 0; i < n; i++) {
        if (n - i - 1 < k - 1) continue;
        rec(n - i - 1, k - 1).forEach((js) => {
            result.push([i, ...js.map((j) => j + i + 1)]);
        });
    }
    return result;
}

//rec関数を使って返ってきた配列をarrayのインデックス番号と見立てて配列の組み合わせを作る
function com(array, num) {
    const last = rec(array.length, num);
    const result = [];
    for (let ite = 0; ite < last.length; ite++) {
        const answer = [];
        for (let fin = 0; fin < last[ite].length; fin++) {
            answer.push(array[last[ite][fin]]);
        }
        result.push(answer);
    }
    return result;
}

コード自体はそこまで長くないが、rec関数ではfor文をfor文で繰り返す回数が動的(語彙力)なので再帰関数を使うのがミソ。これに関しては「組み合わせ 列挙 javascript」でググれば上の方に出てくる記事(こんなのとか)にもあるので詳しくそちらを見ることをお勧めします。正直僕もあまり理解していません。

順列

//順列のコードは分からなかったので他サイト(後述)のほぼコピペ
const per = (array, num) => {
    const result = [];
    if (array.length < num) {
        return [];
    }
    if (num === 1) {
        for (let i = 0; i < array.length; i++) {
            result[i] = [array[i]];
        }
    } else {
        for (let i = 0; i < array.length; i++) {
            const parts = array.slice(0);
            parts.splice(i, 1)[0];
            const row = per(parts, num - 1);
            for (let j = 0; j < row.length; j++) {
                result.push([array[i]].concat(row[j]));
            }
        }
    }
    return result;
};

「組み合わせのrec関数を少し変えれば順列も作れるだろ」と鷹を括っていたものの、実際考えてみると全く思い浮かばず、結局他サイトのコードを少し変えただけという情けない結果に終わりました。このサイトの組み合わせのコードを使えば上のrec関数のくだりも必要ないのですが、流石にそれはあかん。ちなみにこの関数も再帰してますね。

作ったnpmモジュール

このコードを使ってモジュールを作ったので、開発の際に組み合わせ、順列の列挙がしたいという方は是非使ってみてください。

percom

permutation(順列)の"per"とcombination(組み合わせ)の"com"をくっつけた安直な命名。
npmページ
GitHub

環境設定は簡単。

こうして

$ npm i percom

こうするだけ

const percom = require('percom');

組み合わせ

組み合わせを列挙したければ

const array = [1,2,3]//もちろん文字列の配列でも可
const combinations = percom.com(array, 2);
  //combinations = [ [ "A", "B" ], [ "A, "C ], [ "B", "C" ] ]

順列

順列を列挙したければ

const array = [1,2,3]//もちろん文字列の配列でも可
const permutations = percom.per(array, 2);
  //permutations = [ [ 'A', 'B' ], [ 'A', 'C' ], [ 'B', 'A' ], [ 'B', 'C' ], [ 'C', 'A' ], [ 'C', 'B' ] ]

percom.com()もpercom.per()も第一引数に対象の配列、第二引数に列挙する組み合わせの要素数を入力すれば上手くいきます。


一から自分で作ったわけではありませんが割と苦労して作ったモジュールなので是非使ってください。ちなみにpaizaとかAtcoderではどのみちモジュールは使えないのでこのコードをコピペして使ってください。

Twitter

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Deno v1.0.0 がリリースされたということでマニュアルページを翻訳ッ?

Deno

Deno v1.0.0 がリリースされた ということで、Deno Manual のページを翻訳してみました。
Qiita に翻訳記事を掲載するのは適さないのかもしれませんが、日本では一番エンジニアにリーチしそうに思いましたので Qiita に投稿させて頂きました。

ベースにしたバージョンとコミットは以下のものになります。v1.0.0 のものではないですが、ご了承くださいmm

原文では、 Compiler API などの unstable な機能の記述もありましたが、この翻訳では削除しています。

はじめに

Deno は、デフォルトでセキュアな JavaScript/TypeScript ランタイムであり、開発者に素晴らしい経験をもたらします。
Deno は、V8, Rust, Tokio によって作られています。

主な特徴について

  • デフォルトでセキュアです。明示的に有効にしない限り、ファイル、ネットワーク、環境変数等にアクセスすることができません。
  • TypeScript のコードを明示的なコンパイルなしで実行できます。
  • 単一の実行可能なファイル(deno)のみがリリースされます。
  • 依存の検査 (deno info) や コードフォーマット (deno fmt)などを実行する便利なサブコマンドが組み込まれています。
  • Deno での動作が保証されている標準モジュールがあります。
  • 実行に必要な複数のスクリプトファイルを一つの JavaScript ファイルにバンドルする機能があります。

思想

Deno は、現代的なプログラマのための生産性と安全性が高いスクリプティング環境を目指しています。
Deno は、単一の実行可能ファイルでリリースされます。

Given a URL to a Deno program, it is runnable with nothing more than the ~15 megabyte zipped executable.
Deno は、ランタイムとパッケージマネージャーの両方の役割を持ちます。
モジュールの読み込みには、URLを利用した標準のブラウザ互換のプロトコルが使用されます。
Deno は、bash や Python で書かれたユーティリティースクリプトの優れた代替えになります。

ゴール

  • 単一の実行可能ファイル(deno)のみがリリースされる。
  • 安全なデフォルト値を提供する。
    • 特に許可されていない限り、スクリプトはファイル、環境、ネットワークにアクセスできません。
  • ブラウザとの互換性があります。JavaScriptのみで書かれており、グローバルな名前空間 Deno を使用していないDenoプログラムのサブセット(またはそのための機能テスト)は、最新のWebブラウザでも変更なく実行できるようにする必要があります。
  • ユニットテスト、コードフォーマッター、リンターなどの組み込みツールを提供し、開発者の体験を向上させます。
  • Does not leak V8 concepts into user land.
  • 効率的に HTTP を扱える

Node.js との比較

  • Deno は npm を使いません
    • URL やファイルパスでモジュールを参照します。
  • Deno はモジュール解決のアルゴリズムで package.json を使用しません。
  • Deno のすべての非同期アクションは、Promise を返します。Deno は Node.js とは異なる API を提供します。
  • Denoでは、ファイル、ネットワーク、環境変数へのアクセスに明示的な権限が必要です。
  • エラーを捕捉しない場合は、Deno は常に終了します。
  • "ES Modules"を使います。require() はサポートしません。サードパーティモジュールは、URLでインポートできます。
import * as log from "https://deno.land/std/log/mod.ts";

Other key behaviors

  • リモートコードは最初の実行時にダウンロード・キャッシュされ、コードが --reload フラグで実行されるまで更新されません。
  • URL からダウンロードされファイルは、不変でキャッシュ可能である必要があります。

ロゴ

次のロゴは、Deno 本体と同様に、MITライセンスの下で配布されています(パブリックドメインでフリーで使用できます)。

組み込まれているユーティリティーとコマンド

  • 依存検査 (deno info)
  • コードフォーマッター (deno fmt)
  • バンドリング (deno bundle)
  • ランタイムの型情報 (deno types)
  • テストランナー (deno test)
  • デバッガ (--debug)
  • リンター (deno lint) coming soon

セットアップ

Deno は、macOS, Linux, Windows で動作します。
Deno は、単一の実行可能なバイナリです。
外部の依存ファイルはありません。

ダウンロードとインストール

deno_install は、バイナリをダウンロードし、インストールするための便利なスクリプトを提供します。

シェルでインストール:

curl -fsSL https://deno.land/x/install/install.sh | sh

PowerShell でインストール:

iwr https://deno.land/x/install/install.ps1 -useb | iex

Scoop (Windows)でインストール:

scoop install deno

Chocolatey (Windows)でインストール:

choco install deno

Homebrew (macOS)でインストール:

brew install deno

Cargo でインストール:

cargo install deno

Deno のバイナリは、 github.com/denoland/deno/releasesから tar や zip ファイルをダウンロードし、手動でインストールすることもできます。
これらのパッケージには、実行可能ファイルが1つだけ含まれています。

macOSとLinuxでは、実行可能ビットを設定する必要があります。

インストールし、環境変数 PATH を設定し、次を実行してみてください。

deno run https://deno.land/std/examples/welcome.ts

インストール後の確認

インストール後に、deno --version を実行し確認します。
これでDenoバージョンがコンソールに出力されれば、インストールは成功しています。

deno コマンドのオプションと使用方法を確認したい場合は、deno help を実行してください。
サブコマンドのオプションを確認したい場合は、deno help <subcommand> を実行してください。

バージョンアップ

Deno をバージョンアップするには、deno upgrade を実行します。

これは、github.com/denoland/deno/releases から最新のリリースを取得・解凍し、現在の実行ファイルを置き換えます。

ソースからビルド

コントリビュータ向けビルド手順を参照してください。

環境設定

Deno での開発の生産性をあげるために、環境を設定する必要があります。
シェルのオートコンプリート、環境変数、エディタや IDE の設定について説明します。

環境変数

Deno の動作を制御する環境変数があります:

DENO_DIR のデフォルトは $HOME/.deno ですが、生成されたソースコードやキャッシュされたソースコードの書き込み先や読み込み先を変更するために、任意のパスを設定することができます。

NO_COLOR を設定するとカラー出力が無効になります。
https://no-color.org/ を確認してください。
プログラムからは、boolean 定数 Deno.noColor を用いて --allow-env を指定せずに NO_COLOR が設定されているかどうかを確認することができます。

シェル補完

deno completions <shell> コマンドを使用して、シェルの補完スクリプトを生成できます。
このコマンドは標準出力に出力するため、適切なファイルにリダイレクトする必要があります。

サポートシェル:

  • zsh
  • bash
  • fish
  • powershell
  • elvish

例:

deno completions bash > /usr/local/etc/bash_completion.d/deno.bash
source /usr/local/etc/bash_completion.d/deno.bash

エディタと IDEs

Deno のモジュールのインポートは、ファイル拡張子を指定することが可能であり、URL 形式でのインポートが可能です。
現時点では、ほとんどのエディターと Language Server ではこれをサポートしていないため、多くのエディターはエラーとして扱います。

コミュニティはこれらの問題を解決するために、いくつかのエディタ用の拡張機能を開発しました。

Support for JetBrains IDEs is not yet available, but you can follow and upvote these issues to stay up to date:
JetBrains IDE のサポートはまだ利用できませんが、これらの issue をフォローし、vote することができます。

API リファレンス

deno types

Deno のランタイムAPIの正確な情報を取得するには、コマンドラインで次のコマンドを実行します。

$ deno types

出力は、Deno に組み込まれている3つのライブラリファイルを連結したものです。

参考サイト

TypeScript Deno API.

Rust プログラムに Deno を組み込みたい場合は、Rust Deno API を参照してください。

Deno クレートは、crates.io にホストされています。

Examples

Unix の cat コマンドの実装

このプログラムでは、コマンドライン引数をファイル名とみなし、ファイルをオープンして標準出力に出力します。

for (let i = 0; i < Deno.args.length; i++) {
  let filename = Deno.args[i];
  let file = await Deno.open(filename);
  await Deno.copy(file, Deno.stdout);
  file.close();
}

The copy() function here actually makes no more than the necessary kernel -> userspace -> kernel copies.
That is, the same memory from which data is read from the file, is written to stdout.

これは、Deno の I/O ストリームの全般的な設計目標を示しています。

実行:

$ deno run --allow-read https://deno.land/std/examples/cat.ts /etc/passwd

TCP echo server

これは、ポート8080で接続を受け入れ、送信されたものをクライアントに返す単純なサーバーの例です。

const listener = Deno.listen({ port: 8080 });
console.log("listening on 0.0.0.0:8080");
for await (const conn of listener) {
  Deno.copy(conn, conn);
}

このプログラムを起動すると、PermissionDeniedエラーが発生します。

$ deno run https://deno.land/std/examples/echo_server.ts
error: Uncaught PermissionDenied: network access to "0.0.0.0:8080", run again with the --allow-net flag
► $deno$/dispatch_json.ts:40:11
    at DenoError ($deno$/errors.ts:20:5)
    ...

セキュリティ上の理由から、Denoは明示的な許可なしにプログラムがネットワークにアクセスすることを許可していません。

ネットワークへのアクセスを許可するには、コマンドオプションを使用します。

$ deno run --allow-net https://deno.land/std/examples/echo_server.ts

テストするには、netcat を使用してデータを送信してみてください:

$ nc localhost 8080
hello world
hello world

cat.ts の例と同様に、ここでも copy() 関数は不要なメモリのコピーを作成しません。

カーネルからのパケットを受信し、それ以上複雑な処理をせずに送り返します。

権限の確認と取り消し

プログラムによっては、以前に付与された権限を取り消すことが必要になる場合があります。
後の段階でプログラムが権限を必要とする場合、プログラムはエラーになります。

// 権限を確認する
const status = await Deno.permissions.query({ name: "write" });
if (status.state !== "granted") {
  throw new Error("need write permission");
}

const log = await Deno.open("request.log", "a+");

// 一部の権限を取り消す
await Deno.permissions.revoke({ name: "read" });
await Deno.permissions.revoke({ name: "write" });

// ログファイルを使う
const encoder = new TextEncoder();
await log.write(encoder.encode("hello\n"));

// これはエラーになります
await Deno.remove("request.log");

File server

これは、HTTPサーバとしてローカルディレクトリを公開します。

deno install --allow-net --allow-read https://deno.land/std/http/file_server.ts

実行:

$ file_server .
Downloading https://deno.land/std/http/file_server.ts...
[...]
HTTP server listening on http://0.0.0.0:4500/

また、最新のバージョンにアップグレードしたい場合は、次のようにします:

$ file_server --reload

指定したモジュールの再読み込み

一部のモジュールのみをアップグレードしたい場合があります。--reload オプションで対象を指定することにより制御できます。

すべての再読み込み

--reload

すべての標準モジュールの再読み込み

--reload=https://deno.land/std

特定のモジュール(この例では、colors とファイルシステムユーティリティ)を再ロードするには、カンマを使用してURLを区切ります。

--reload=https://deno.land/std/fs/utils.ts,https://deno.land/std/fmt/colors.ts

ホワイトリストによる権限指定

Deno は、ホワイトリストによる権限を指定する方法も提供しています。

これは、ホワイトリストでファイルシステムへのアクセスを制限する例です。

$ deno run --allow-read=/usr https://deno.land/std/examples/cat.ts /etc/passwd
error: Uncaught PermissionDenied: read access to "/etc/passwd", run again with the --allow-read flag
► $deno$/dispatch_json.ts:40:11
    at DenoError ($deno$/errors.ts:20:5)
    ...

/etc ディレクトリ配下の読み取り権限を付与できます。

$ deno run --allow-read=/etc https://deno.land/std/examples/cat.ts /etc/passwd

--allow-write は、 --allow-read と同様に使えます。

次は、ホスト名でネットワークへのアクセスを制限する例です。

const result = await fetch("https://deno.land/");
$ deno run --allow-net=deno.land https://deno.land/std/examples/curl.ts https://deno.land/

子プロセスの実行

API リファレンス

例:

// 子プロセスを生成する
const p = Deno.run({
  cmd: ["echo", "hello"],
});

// 完了を待機します
await p.status();

実行:

$ deno run --allow-run ./subprocess_simple.ts
hello

ここで関数が 'window.onload' に割り当てられます。
この関数は、メインスクリプトが読み込まれたあとに呼ばれます。
これは、Webブラウザの onload と同じでエントリポイントとして利用できます。

Deno.run() で子プロセスを生成すると、デフォルトで 親プロセスの stdin, stdout, stderr を引き継ぎます。

子プロセスと通信したい場合は、"piped" オプションが使用できます。

const fileNames = Deno.args;

const p = Deno.run({
  cmd: [
    "deno",
    "run",
    "--allow-read",
    "https://deno.land/std/examples/cat.ts",
    ...fileNames,
  ],
  stdout: "piped",
  stderr: "piped",
});

const { code } = await p.status();

if (code === 0) {
  const rawOutput = await p.output();
  await Deno.stdout.write(rawOutput);
} else {
  const rawError = await p.stderrOutput();
  const errorString = new TextDecoder().decode(rawError);
  console.log(errorString);
}

Deno.exit(code);

実行:

$ deno run --allow-run ./subprocess.ts <somefile>
[file content]

$ deno run --allow-run ./subprocess.ts non_existent_file.md

Uncaught NotFound: No such file or directory (os error 2)
    at DenoError (deno/js/errors.ts:22:5)
    at maybeError (deno/js/errors.ts:41:12)
    at handleAsyncMsgFromRust (deno/js/dispatch.ts:27:17)

シグナルの捕捉

API リファレンス

シグナルの捕捉には Deno.signal() 関数を使います。

for await (const _ of Deno.signal(Deno.Signal.SIGINT)) {
  console.log("interrupted!");
}

Deno.signal() は、プロミスとしても動作します。

await Deno.signal(Deno.Singal.SIGINT);
console.log("interrupted!");

シグナルの捕捉を止めたい場合、signal オブジェクトの dispose() メソッドを使います。

const sig = Deno.signal(Deno.Signal.SIGINT);
setTimeout(() => { sig.dispose(); }, 5000);

for await (const _ of sig) {
  console.log("interrupted");
}

上記の for-await ループは、5秒後に sig.dispose()が呼ばれ、終了します。

ファイルシステムイベント

ファイルシステムのイベントをポーリングするには:

const watcher = Deno.watchFs("/");
for await (const event of watcher) {
  console.log(">>>> event", event);
  // { kind: "create", paths: [ "/foo.txt" ] }
}

イベントの正確な順序は、オペレーティングシステムによって異なる場合があることに注意してください。
この機能は、プラットフォームによって異なるシステムコールを使用します。

  • Linux: inotify
  • macOS: FSEvents
  • Windows: ReadDirectoryChangesW

サードパーティコードのインポート

前述の例では、Deno が URL からスクリプトを実行できることがわかりました。
Web ブラウザ上の JavaScript と同様に、Deno は URL から直接ライブラリをインポートできます。
次のコードは、URL を使用してアサーションライブラリをインポートしています。

import { assertEquals } from "https://deno.land/std/testing/asserts.ts";

Deno.test("t1", function () {
  assertEquals("hello", "hello");
});

Deno.test("t2", function () {
  assertEquals("world", "world");
});

実行:

$ deno run test.ts
running 2 tests
test t1 ... ok
test t2 ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

このプログラムの実行に --allow-netフラグを指定する必要はないですが、ネットワークにアクセスにしています。
ランタイムは、import で指定したファイルをダウンロードしディスクにキャッシュするための特別なアクセス権を持っています。

Denoは、環境変数 $DENO_DIR で指定されたディレクトリに import で指定したファイルをキャッシュします。
$DENO_DIR が指定されていない場合、デフォルトでシステムのキャッシュディレクトリになります。
次にプログラムを実行するときには、ダウンロードは行われません。
プログラムが変更されていなければ、再コンパイルもされません。

ディレクトリのデフォルト:

  • Linux/Redox: $XDG_CACHE_HOME/deno または $HOME/.cache/deno
  • Windows: %LOCALAPPDATA%/deno (%LOCALAPPDATA% = FOLDERID_LocalAppData)
  • macOS: $HOME/Library/Caches/deno
  • If something fails, it falls back to $HOME/.deno

しかし、 https://deno.land/ がダウンした場合はどうなりますか?

外部サーバーに依存することは、開発には便利ですが、本番環境では脆弱です。

Production software should always bundle its dependencies.

本番のソフトウェアは常に依存関係をバンドルする必要があります。
Deno では、$DENO_DIRディレクトリでダウンロードした依存ファイルが管理されます。$DENO_DIRは、環境変数として指定できます。

変更される可能性のあるURLを信頼するにはどうすればよいですか。

ロックファイルを使用(--lockオプションを指定)することで、期待どおりのコードを実行しているか確認できます。

特定のバージョンをどのようにインポートしますか?

URLにバージョンを指定するだけです。
たとえば、次のURLは実行されるコードを完全に指定しています: https://unpkg.com/liltest@0.0.5/dist/liltest.js

本番環境で $DENO_DIR にコードを保存する前述の手法と組み合わせると、実行される正確なコードを完全に指定し、ネットワークにアクセスせずにコードを実行できます。

同じURLをそれぞれのファイルでインポートするのは扱いにくくありませんか。URLの1つが異なるバージョンのライブラリにリンクしている場合はどうなりますか? 大規模なプロジェクトで、至るところで同じURLを書いてしまうと問題が発生しませんか?

解決策としては、 deps.ts ファイル(Node.js のpackage.jsonファイルと同じ目的を果たす)を作成し、外部ライブラリをインポートおよび再エクスポートすることです。
例えば、大規模なプロジェクトで上記のアサーション・ライブラリを使用していたとします。
"https://deno.land/std/testing/asserts.ts"を各ファイルでインポートするのではなく、サードパーティのコードをエクスポートする deps.tsファイルを作成し利用できます。

export {
  assert,
  assertEquals,
  assertStrContains,
} from "https://deno.land/std/testing/asserts.ts";

同じプロジェクト内では、 deps.tsからインポートして同じURLへの多くの参照を避けることができます:

import { assertEquals, runTests, test } from "./deps.ts";

この設計により、パッケージ管理ソフトウェア、中央集権型コードリポジトリ、余計なファイル形式が生み出す膨大な複雑さを回避できます。

型定義ファイルの利用

Deno のランタイムは、ファーストクラスの言語として JavaScript と TypeScript をサボーとしています。
つまり、拡張子(または正しいメディアタイプを提供するサーバー)を含む完全修飾モジュール名が必要です。
Deno には曖昧なモジュール解決はありません。

TypeScript コンパイラは、JavaScript モジュールに型を適用するために、拡張子のないモジュールと Node.js モジュール解決ロジックの両方に依存しています。

このギャップを埋めるために、Deno は、曖昧な解決に頼ることなく型定義ファイルを参照する3つの方法をサポートしています。

コンパイラ ヒント

JavaScript モジュールをインポートしていて、そのモジュールの型定義の場所が分かってる場合は、インポート時に型定義を指定できます。

これはコンパイラのヒントの形式を使います。
コンパイラーのヒントは、.d.ts ファイルの場所と、関連付けられている JavaScript コードを Deno に伝えます。
The hint is @deno-types and when specified the value will be used in the compiler instead of the JavaScript module.

このヒントは @deno-types であり、指定された値はJavaScriptモジュールの代わりにコンパイラで使用されます。

例えば、 foo.js があり、その型定義ファイル foo.d.tsである場合、コードは次のようになります:

// @deno-types="./foo.d.ts"
import * as foo from "./foo.js";

型定義ファイルを指定するパスは、モジュールのインポートと同じロジックで解決されます。つまり拡張子が必要で JavaScript フィアルと対応している必要があります。
リモート上のファイルを指定することも可能です。

このヒントは次の行の import 文 (または export ... from 文) に影響し、コンパイル時に指定されたモジュールの代わりに @deno-types の内容が使われます。

上記の例では、Deno コンパイラは './foo.js' の代わりに './foo.d.ts' を読み込みます。
Deno は、プログラムを実行するときに './foo.js' を読み込みます。

JavaScript ファイルのトリプルスラッシュ の リファレンスディレクティブ

Deno で使用したいモジュールをホスティングしていて型定義ファイルの場所を Deno に認識させたい場合は、コードの中でトリプルスラッシュディレクティブを利用することができます。
例えば、JavaScriptモジュールがあり、そのファイルと一緒にある型定義の場所を Deno に認識させたいとします。
foo.js は以下のようになるかもしれません。

/// <reference types="./foo.d.ts" />
export const foo = "foo";

Denoはこれを認識し、コンパイラーはファイルの型チェック時に foo.d.ts を使用しますが、foo.js は実行時に読み込まれます。
ディレクティブは、モジュールのインポートと同じロジックで解決されます。つまり拡張子が必要で JavaScript ファイルと対応している必要があります。
リモート上のファイルを指定することも可能です。

カスタムヘッダー X-TypeScript-Types

使用するモジュールをホストしている場合は、カスタム HTTP ヘッダー X-TypeScript-Types を使用して、型定義ファイルの場所を Deno に認識させることができます。

このヘッダーは、前述のトリプルスラッシュリファレンスと同じように機能しますが、JavaScriptファイル自体のコンテンツを変更する必要がなく、型定義の場所をサーバー自体が決定できることを意味します。

すべての型定義ファイルがサポートされているわけではありません。

Deno はコンパイラヒントを使って指定された .d.ts ファイルをロードしますが、.d.ts ファイルの中にはサポートされていない機能が含まれているものもあります。
具体的には、.d.ts ファイルの中には、モジュールの解決ロジックを使って他のパッケージから型定義をロードしたり参照したりしているものがあります。
例えば、node を含む型参照ディレクティブは、./node_modules/@types/node/index.d.ts のようなパスに解決することを期待しています。
Since this depends on non-relative "magical" resolution, Deno cannot resolve this.

TypeScript ファイルでトリプルスラッシュの型参照を使用しないのはなぜですか?

TypeScript コンパイラは、型参照ディレクティブを含むトリプルスラッシュディレクティブをサポートしています。
Deno がこれを使うと TypeScript コンパイラの動作に支障をきたします。
Deno は JavaScript(とJSX)ファイル内のディレクティブのみを検索します。

現在のファイルがメインプログラムであるかどうかの判定

現在のスクリプトがメインプログラムとして実行されたかどうかを判定するには、import.meta.mainをチェックしてください。

if (import.meta.main) {
  console.log("main");
}

コマンドラインインターフェース

V8 フラグ

V8 には多くの内部コマンドラインフラグがあります。

# 使用可能な V8 フラグを確認する
$ deno --v8-flags=--help

# 複数のフラグを使用する例
$ deno --v8-flags=--expose-gc,--use-strict

次のフラグは特に便利です。

--async-stack-trace

バンドル

deno bundle <source_file> は、指定された source_file のすべての依存関係を含む単一の JavaScript ファイルを出力します。

例:

> deno bundle https://deno.land/std/examples/colors.ts colors.bundle.js
Bundling "colors.bundle.js"
Emitting bundle to "colors.bundle.js"
9.2 kB emitted.

パラメータの出力ファイルを省略すると、stdout に出力されます。

出力されたバンドルファイルは、他のモジュールと同様に実行できます:

deno run colors.bundle.js

出力されたバンドルファイルは自己完結型 ES モジュールで、コマンドラインで指定されたメインモジュールからのエクスポートが使用可能になります。

export { foo } from "./foo.js";

export const bar = "bar";

次のようにインポートできます:

import { foo, bar } from "./lib.bundle.js";

バンドルされたファイルは、Webブラウザでも読み込むことができます。
自己完結型のESモジュールであるため、 type の属性は "module" に設定する必要があります。

例:

<script type="module" src="website.bundle.js"></script>

または、別のESモジュールにインポートして使用することもできます:

<script type="module">
  import * as website from "website.bundle.js";
</script>

実行可能なスクリプトのインストール

Denoは、実行可能なファイルを簡単にインストールするための deno install を提供します。

deno install [OPTIONS ...] [EXE_NAME] [URL] [SCRIPT_ARGS ...] は、 URL で利用可能なスクリプトを指定し、EXE_NAME という名前でインストールします。

このコマンドは、指定されたCLIオプションとメインモジュールを使用して denoを呼び出す、小さい実行可能なシェルスクリプトを作成します。
シェルスクリプトは、インストールルートの bin ディレクトリ配下に配置されます。

例:

$ deno install --allow-net --allow-read https://deno.land/std/http/file_server.ts
[1/1] Compiling https://deno.land/std/http/file_server.ts

✅ Successfully installed file_server.
/Users/deno/.deno/bin/file_server

実行ファイル名を変更するには、-n/--name オプションを使います:

  deno install --allow-net --allow-read -n serve https://deno.land/std/http/file_server.ts

実行ファイル名が省略される場合、デフォルトで決定します:

  • URL パスの最下層の名前を使用します。上記の例は file_server になります。
  • If the file stem is something generic like 'main', 'mod', 'index' or 'cli', and the path has no parent, take the file name of the parent path. Otherwise settle with the generic name.

インストールルートを変更するには、--root オプションを使用します。

$ deno install --allow-net --allow-read --root /usr/local https://deno.land/std/http/file_server.ts

インストールルートは、次の優先順位で決定します:

  • --root オプション
  • 環境変数 DENO_INSTALL_ROOT
  • $HOME/.deno

必要に応じて手動でパスに追加する必要があります。

$ echo 'export PATH="$HOME/.deno/bin:$PATH"' >> ~/.bashrc

インストール時にスクリプト実行に必要な権限を指定する必要があります。

$ deno install --allow-net --allow-read https://deno.land/std/http/file_server.ts 8080

上記のコマンドは、参照権限で実行しポート8080にバインドする file_server という名前の実行可能ファイルを作成します。

import.meta.main を使って、実行可能なスクリプトのエントリポイントを指定すると良いでしょう。

例:

// https://example.com/awesome/cli.ts
async function myAwesomeCli(): Promise<void> {
  -- snip --
}

if (import.meta.main) {
  myAwesomeCli();
}

実行可能なスクリプトに関してユーザに共有する際は、インストールコマンドをドキュメントに記載してください。

# Install using deno install

$ deno install -n awesome_cli https://example.com/awesome/cli.ts

プロキシ

Denoは、モジュールのダウンロードと fetch API のためのプロキシをサポートしています。

プロキシの設定は環境変数 HTTP_PROXYHTTPS_PROXY から読み込みます。

Windows で環境変数が見つからない場合、レジストリからプロキシ情報を読み出します。

ロックファイル

Denoは、小さなJSONファイルを使用して、モジュールのサブリソースの整合性を保存およびチェックできます。
ロックファイルのチェックを有効し指定するには、--lock=lock.json オプションを使用します。
ロックファイルを更新または作成するには --lock=lock.json --lock-write オプションを使用します。

Import maps

Deno は、 import maps をサポートします。

deno コマンドの --importmap=<FILE> オプションで import map を指定できます。

現在の制限:

  • 指定可能な import map は、一つのみです
  • フォールバックURLは使えません
  • ネームスペース std: をサポートしていません
  • スキームは、file:, http:, https: のみサポートされます

例:

// import_map.json

{
   "imports": {
      "http/": "https://deno.land/std/http/"
   }
}
// hello_server.ts

import { serve } from "http/server.ts";

const body = new TextEncoder().encode("Hello World\n");
for await (const req of serve(":8000")) {
  req.respond({ body });
}
$ deno run --importmap=import_map.json hello_server.ts

WASM サポート

Deno は、 wasm バイナリを実行できます。

const wasmCode = new Uint8Array([
  0, 97, 115, 109, 1, 0, 0, 0, 1, 133, 128, 128, 128, 0, 1, 96, 0, 1, 127,
  3, 130, 128, 128, 128, 0, 1, 0, 4, 132, 128, 128, 128, 0, 1, 112, 0, 0,
  5, 131, 128, 128, 128, 0, 1, 0, 1, 6, 129, 128, 128, 128, 0, 0, 7, 145,
  128, 128, 128, 0, 2, 6, 109, 101, 109, 111, 114, 121, 2, 0, 4, 109, 97,
  105, 110, 0, 0, 10, 138, 128, 128, 128, 0, 1, 132, 128, 128, 128, 0, 0,
  65, 42, 11
]);
const wasmModule = new WebAssembly.Module(wasmCode);
const wasmInstance = new WebAssembly.Instance(wasmModule);
console.log(wasmInstance.exports.main().toString());

WASM ファイルは、import 文でも読み込むことができます:

import { fib } from "./fib.wasm";
console.log(fib(20));

TypeScript のコンパイラオプション

Deno エコシステムを TypeScript の理想的な状態にするために、すべての strict フラグをデフォルトで有効にしています。
ただし、プログラムの実行時に tsconfig.json などの設定ファイルをオプションで指定することによりカスタマイズすることが可能です。
アプリケーションを実行するときに -c オプションを使い、設定ファイルを指定することができます。

deno run -c tsconfig.json mod.ts

設定可能な項目とデフォルトは、次のようになります:

{
  "compilerOptions": {
    "allowJs": false,
    "allowUmdGlobalAccess": false,
    "allowUnreachableCode": false,
    "allowUnusedLabels": false,
    "alwaysStrict": true,
    "assumeChangesOnlyAffectDirectDependencies": false,
    "checkJs": false,
    "disableSizeLimit": false,
    "generateCpuProfile": "profile.cpuprofile",
    "jsx": "react",
    "jsxFactory": "React.createElement",
    "lib": [],
    "noFallthroughCasesInSwitch": false,
    "noImplicitAny": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noImplicitUseStrict": false,
    "noStrictGenericChecks": false,
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "preserveConstEnums": false,
    "removeComments": false,
    "resolveJsonModule": true,
    "strict": true,
    "strictBindCallApply": true,
    "strictFunctionTypes": true,
    "strictNullChecks": true,
    "strictPropertyInitialization": true,
    "suppressExcessPropertyErrors": false,
    "suppressImplicitAnyIndexErrors": false,
    "useDefineForClassFields": false
  }
}

許可される値と使用例に関するドキュメントについては、TypeScript のドキュメントを参照してください。

注:上記にリストされていないオプションは、Deno でサポートされていないか、TypeScriptドキュメントで非推奨/実験的としてリストされています。

プログラムのライフサイクル

Deno は、Web ブラウザの JavaScript と互換のあるライフサイクルイベントをサポートしています: loadunload.

これらのイベントを使用し、プログラムのセットアップとクリーンアップのコードを作成することができます。

loadイベントのリスナーは非同期であり、待機します。
unload イベントのリスナーは同期している必要があります。
どちらのイベントもキャンセルできません。

例:

// main.ts
import "./imported.ts";

const handler = (e: Event): void => {
  console.log(`got ${e.type} event in event handler (main)`);
};

window.addEventListener("load", handler);

window.addEventListener("unload", handler);

window.onload = (e: Event): void => {
  console.log(`got ${e.type} event in onload function (main)`);
};

window.onunload = (e: Event): void => {
  console.log(`got ${e.type} event in onunload function (main)`);
};

// imported.ts
const handler = (e: Event): void => {
  console.log(`got ${e.type} event in event handler (imported)`);
};

window.addEventListener("load", handler);
window.addEventListener("unload", handler);

window.onload = (e: Event): void => {
  console.log(`got ${e.type} event in onload function (imported)`);
};

window.onunload = (e: Event): void => {
  console.log(`got ${e.type} event in onunload function (imported)`);
};

console.log("log from imported script");

window.addEventListenerwindow.onload/window.onunload の両方を使用してイベントのハンドラーを定義できることに注意してください。
そこには大きな違いがあります。次の例を実行してみましょう:

$ deno run main.ts
log from imported script
log from main script
got load event in onload function (main)
got load event in event handler (imported)
got load event in event handler (main)
got unload event in onunload function (main)
got unload event in event handler (imported)
got unload event in event handler (main)

window.addEventListener を使用して追加されたすべてのリスナーが実行されましたが、window.onload および window.onunload に関しては、imported.ts で定義されたものを main.ts の定義がオーバーライドしています。

deno fmt

Deno ships with a built in code formatter that auto-formats TypeScript and JavaScript code.
Denoには、TypeScript や JavaScript のコードを自動でフォーマットするコードフォーマッタが組み込まれています。

# 現在のディレクトリとサブディレクトリのすべての JS/TS ファイルをフォーマットする
deno fmt
# 指定したファイルをフォーマットする
deno fmt myfile1.ts myfile2.ts
# 現在のディレクトリとサブディレクトリのすべての JS/TS ファイルがフォーマットされているかどうかをチェックします
deno fmt --check
# 標準入力をフォーマットし、標準出力に書き込む
cat file.ts | deno fmt -

コードの前に // deno-fmt-ignore コメントをつけることでフォーマット対象から外します:

// deno-fmt-ignore
export const identity = [
    1, 0, 0,
    0, 1, 0,
    0, 0, 1,
];

また、ファイルの先頭に // deno-fmt-ignore-file コメントを追加することで、ファイル全体をフォーマット対象から外すことができます。

内部の詳細

Deno と Linux の類否

Linux Deno
Processes Web Workers
Syscalls Ops
File descriptors (fd) Resource ids (rid)
Scheduler Tokio
Userland: libc++ / glib / boost https://deno.land/std/
/proc/\$\$/stat Deno.metrics()
man pages deno types

Resources

Resources(別名 rid)は、File descriptor の Deno バージョンです。
これは、オープンファイル、ソケット、その他のものを参照するために使用される整数値です。
次のコードでは、オープンしている Resources を確認しています。

const { resources, close } = Deno;
console.log(resources());
// { 0: "stdin", 1: "stdout", 2: "stderr" }
close(0);
console.log(resources());
// { 1: "stdout", 2: "stderr" }

メトリクス

メトリクスは、様々な統計情報のための内部カウンターです。

> console.table(Deno.metrics())
┌──────────────────┬────────┐
│     (index)      │ Values │
├──────────────────┼────────┤
│  opsDispatched   │   9    │
│   opsCompleted   │   9    │
│ bytesSentControl │  504   │
│  bytesSentData   │   0    │
│  bytesReceived   │  856   │
└──────────────────┴────────┘

Schematic diagram

Profiling

To start profiling,

# Make sure we're only building release.
# Build deno and V8's d8.
ninja -C target/release d8

# Start the program we want to benchmark with --prof
./target/release/deno run --allow-net --v8-flags=--prof tests/http_bench.ts &

# Exercise it.
third_party/wrk/linux/wrk http://localhost:4500/
kill `pgrep deno`

V8 will write a file in the current directory that looks like this: isolate-0x7fad98242400-v8.log. To examine this file:

D8_PATH=target/release/ ./third_party/v8/tools/linux-tick-processor
isolate-0x7fad98242400-v8.log > prof.log
# on macOS, use ./third_party/v8/tools/mac-tick-processor instead

prof.log will contain information about tick distribution of different calls.

To view the log with Web UI, generate JSON file of the log:

D8_PATH=target/release/ ./third_party/v8/tools/linux-tick-processor
isolate-0x7fad98242400-v8.log --preprocess > prof.json

Open third_party/v8/tools/profview/index.html in your browser, and select prof.json to view the distribution graphically.

Useful V8 flags during profiling:

  • --prof
  • --log-internal-timer-events
  • --log-timer-events
  • --track-gc
  • --log-source-code
  • --track-gc-object-stats

To learn more about d8 and profiling, check out the following links:

LLDB を使用したデバッグ

LLDB を使用して Deno をデバッグできます。

$ lldb -- target/debug/deno run tests/worker.js
> run
> bt
> up
> up
> l

Rust コードをデバッグするには、 rust-lldb が使用できます。
それは、LLDB のラッパーであり、Rust の開発環境に含まれています。

$ rust-lldb -- ./target/debug/deno run --allow-net tests/http_bench.ts
# On macOS, you might get warnings like
# `ImportError: cannot import name _remove_dead_weakref`
# In that case, use system python by setting PATH, e.g.
# PATH=/System/Library/Frameworks/Python.framework/Versions/2.7/bin:$PATH
(lldb) command script import "/Users/kevinqian/.rustup/toolchains/1.36.0-x86_64-apple-darwin/lib/rustlib/etc/lldb_rust_formatters.py"
(lldb) type summary add --no-value --python-function lldb_rust_formatters.print_val -x ".*" --category Rust
(lldb) type category enable Rust
(lldb) target create "../deno/target/debug/deno"
Current executable set to '../deno/target/debug/deno' (x86_64).
(lldb) settings set -- target.run-args  "tests/http_bench.ts" "--allow-net"
(lldb) b op_start
(lldb) r

Deno コア

Deno のコアは、スタンドアローン crate としてリリースされます。
V8 自体がコアであり、"libdeno" という バインディング API があります。
詳細については、crate のドキュメントを参照してください。

継続的なベンチマークの計測

こちらからベンチマークを参照してください。

ベンチマークチャートは、//website/data.jsonBenchmarkData[] 型を持つものとします。
BenchmarkData は次のように定義されています。

interface ExecTimeData {
  mean: number;
  stddev: number;
  user: number;
  system: number;
  min: number;
  max: number;
}

interface BenchmarkData {
  created_at: string;
  sha1: string;
  benchmark: {
    [key: string]: ExecTimeData;
  };
  binarySizeData: {
    [key: string]: number;
  };
  threadCountData: {
    [key: string]: number;
  };
  syscallCountData: {
    [key: string]: number;
  };
}

コントリビュート

開発

レポジトリの clone

Linux や Mac での clone:

git clone --recurse-submodules https://github.com/denoland/deno.git

Windows のための追加の手順:

  1. "Developer Mode" を有効にしてください (シンボリックリンクは管理者権限を必要とします).
  2. git のバージョンが 2.19.2.2.windows.1 以降であることを確認してください。
  3. checkout の前に、core.symlinks=true を設定してください。 bash git config --global core.symlinks true git clone --recurse-submodules https://github.com/denoland/deno.git

前提条件

Denoをビルドする最も簡単な方法は、V8のプリコンパイルバージョンを使用することです:

cargo build -vv

ソースコードから Deno と V8 をビルドしたい場合は、次のようになります:

V8_FROM_SOURCE=1 cargo build -vv

V8 をソースコードからビルドする場合、さらに次の依存があります:

python または python.exePATH 上に存在し、Python 3 ではなく Python 2 を参照していることを確認してください。

Linux の場合、glib-2.0 の開発ファイルもインストールする必要があります(Ubuntuの場合は、apt install libglib2.0-dev を実行してください)。

Mac の場合、XCode をインストールする必要があります。

Windows の場合:

  1. VS Community 2019 と Desktop development with C++ toolkit を取得し、 必須ツールと以下のすべての C++ ツールを選択してください。
  • Visual C++ tools for CMake
  • Windows 10 SDK (10.0.17763.0)
  • Testing tools core features - Build Tools
  • Visual C++ ATL for x86 and x64
  • Visual C++ MFC for x86 and x64
  • C++/CLI support
  • VC++ 2015.3 v14.00 (v140) toolset for desktop
  1. "Debugging Tools for Windows" を有効にしてください。 "Control Panel" → "Programs" → "Programs and Features" → Select "Windows Software Development Kit - Windows 10" → "Change" → "Change" → Check "Debugging Tools For Windows" → "Change" -> "Finish" と進みます。 または、 Debugging Tools for Windows を使用します。(注意: ファイルをダウンロードし、手動で X64 Debuggers And Tools-x64_en-us.msi をインストールする必要があります)

V8 のビルドについての詳細は、rusty_v8's README を参照してください。

ビルド

Cargo でビルドします:

# ビルド
cargo build -vv

# ビルドエラーが発生する場合、最新のマスターを使用していることを確認してからビルドをやり直してみてください
cargo clean && cargo build -vv

# 実行:
./target/debug/deno run cli/tests/002_hello.ts

Testing and Tools

Test deno:

# Run the whole suite:
cargo test

# Only test cli/js/:
cargo test js_unit_tests

Test std/:

cargo test std_tests

Lint the code:

./tools/lint.py

Format the code:

./tools/format.py

プルリクエストの作成

プルリクエストを作成する前に、次の事が完了しているか確認してください。

  1. 関連する Issue があり、それがPR文中で参照されていること
  2. 変更をカバーするテストがある
  3. cargo test が成功している
  4. tools/format.py でコードをフォーマットしている
  5. ./tools/lint.py が成功している

third_party の変更

deno_third_party では、何が利用されているか把握できるように、Deno に依存する外部コードを管理しています。
手作業とプライベートスクリプトにより慎重に管理されます。
変更を加えるためには、 @ry@piscisaureus の助けが必要になる可能性があります。

Ops の追加(aka bindings)

新しいAPIを追加するときに間違いを犯すことを非常に心配しています。
Deno に Op を追加する際には、他のプラットフォームでの該当インターフェースを調べる必要があります。
この機能が Go、Node、Rust、Python でどのようになっているかを列挙してください。

例として、PR #671Deno.rename() がどのように提案され、追加されたかを参照してください。

API のドキュメント作成

公開 API のドキュメントを作成することは重要であり、コードのインラインでそれを行いたいと考えています。
これにより、コードとドキュメントが緊密に結合されていることを確認することができます。

JSDoc の活用

deno モジュールとグローバル/window 名前空間を介して公開されているすべてのAPIとタイプには、JSDoc ドキュメントが必要です。
このドキュメントは解析され、TypeScript コンパイラで利用できるため、さらに他の場所で利用するのは簡単です。
JSDoc ブロックは、対象のコードの直前にあり、 /** で開始し */で終了します。

例:

/** A simple JSDoc comment */
export const FOO = "foo";
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Azure画像認識系の機能をいくつか試してみた(Face API / Custom Vision API / Computer Vision API)

私自身初めてMicrosoftのAzureを使ってみました。
ここでは、Azureを使う為にやることと、Face API / Custom Vision API / Computer Vision API の使い方や応用サンプルをまとめます。

1.Microsoft Azureを使ってみる

初めて使う人のための手順を簡単にメモ。

1-1.outlook アドレスを取得する

持ってない場合は下記を参考にアドレスを取得する。
https://qiita.com/TomoyoH/items/fca3f49729d82d9884eb

1-2.Microsoft Learnページにサインインする

下記ページにサインインする。
https://docs.microsoft.com/ja-jp/learn/

1-3.Azureアカウントを作成する

下記ページを参考にAzureアカウントを作成する。
https://docs.microsoft.com/ja-jp/learn/modules/create-an-azure-account/3-exercise-create-an-azure-account
無料版での利用でもクレジットカードの情報も求められる。

これで、Azureを使うための準備はできました!

2.Face API(detect & emotion)

2-0.参考

2-1.準備

ここを参考に「キー1」と「エンドポイント」を取得してメモっておく。
https://docs.microsoft.com/ja-jp/learn/modules/identify-faces-with-computer-vision/7-obtain-subscription-keys

取得できたら、下記ページで使っているリージョンを選択して試してみる。
https://westus.dev.cognitive.microsoft.com/docs/services/563879b61984550e40cbbe8d/
image.png

Ocp-Apim-Subscription-Keyに先ほどの「キー1」を入れる。
image.png

その時、この画像のurlはリンク切れしてるので、自分で適当な画像のurlに張り替えて試してください。
image.png

これで、取得したキーを使ってFace APIを試すことができたので、次に応用です。

2-2.応用サンプル

LINEに画像を送ると、人数と感情を返します。
※時々、うまく結果が返ってこない画像があったり、感情も1人分しかとってません。


↑2倍速に編集してます。
'use strict';

const express = require('express');
const line = require('@line/bot-sdk');
const axios = require('axios');
const PORT = process.env.PORT || 3000;

const config = {
    channelSecret: '{channelSecretを入力}',
    channelAccessToken: '{channelAccessTokenを入力}'
};

const app = express();

app.post('/webhook', line.middleware(config), (req, res) => {
    console.log(req.body.events);
    Promise
      .all(req.body.events.map(handleEvent))
      .then((result) => res.json(result));
});

const client = new line.Client(config);

function handleEvent(event) {
  console.log(event);

  // 今回は画像投稿を通す
  if (event.type !== 'message' || event.message.type !== 'image' ) {
    return Promise.resolve(null);
  }

  let mes = ''
  if(event.message.type === 'image'){
    mes = '何人か数えています...!';
    getFaceDetect2(event.message.id, event.source.userId, config.channelAccessToken );
  } else{
    mes = event.message.text;
  }

  return client.replyMessage(event.replyToken, {
    type: 'text',
    text: mes
  });
}

const getFaceDetect2 = async (messageId , userId, accessToken) => {
  console.log("getFaceDetect2");
  console.log(messageId);
  console.log(accessToken);

  ///////// 画像取得 /////////////////////////////////////////////////////

  const configImageAPI = {
    url: `https://api.line.me/v2/bot/message/${messageId}/content`,
    method: 'get',
    headers: {
        'Authorization': 'Bearer ' + accessToken,
    },
    responseType: 'arraybuffer'
  };

  let responseImageAPI;
  try {
    // axios はRequest Configで受信データのタイプを設定できる。今回は arraybuffer が適切。
    responseImageAPI = await axios.request(configImageAPI);
    console.log('image data get');
  } catch (error) {
    console.log('post Error');
    console.error(error);
  }

  ///////// Face API //////////////////////////////////////////////

  const FACE_API_ENDPOINT_URL = ' {準備で取得したエンドポイント} + /face/v1.0/detect?returnFaceId=true&returnFaceLandmarks=false&returnFaceAttributes=emotion&recognitionModel=recognition_01&returnRecognitionModel=false&detectionModel=detection_01';

  // サブスクリプションをOcp-Apim-Subscription-Keyヘッダーに
  // JSONで送るのでContent-typeヘッダーにapplication/octet-stream指定
  const configFaceAPI = {
    url: FACE_API_ENDPOINT_URL,
    method: 'post',
    headers: {
      'Content-type': 'application/octet-stream',
      'Ocp-Apim-Subscription-Key':'{key1を入力}'
    },
    data: responseImageAPI.data
  };

  // axios
  let responseAzure;
  try {
      // POSTリクエストで送る
      responseAzure = await axios.request(configFaceAPI);
      //console.log('post OK');
      // データ送信が成功するとレスポンスが来る
      console.log(responseAzure.data);

  } catch (error) {
      console.log('post Error');
      // ダメなときはエラー
      console.error(error);
  }

  const countPerson = responseAzure.data.length;

  if(responseAzure.data[0].faceAttributes){
  // とりあえず[0]でひとり目の感情をとってくる。
  var emo = responseAzure.data[0].faceAttributes.emotion;
  var type;
  var val = 0;
  // 一番大きな感情を返す
  for(let key in emo){
      if(emo[key] > val){
          val = emo[key];
          type = key ;
      }
  }
}

  val = Math.round(val *100);
  console.log(emo);

  // 返信
  await client.pushMessage(userId, {
    type: 'text',
    text: `この中には ${countPerson} 人いるよ!` ,
  });

  await client.pushMessage(userId, {
    type: 'text',
    text: `感情は${type}${val}%だよ。` ,
  });

}

app.listen(PORT);
console.log(`Server running at ${PORT}`);

LINEbot自体の基本的な説明は下記を参考にしてみてください。
https://qiita.com/n0bisuke/items/ceaa09ef8898bee8369d

3.Custom Vision API

3-0.参考

3-1.準備

まずは「リソースの作成」からcustom visionのリソースグループやリソースを作ります。
image.png
custom visionを探して、目のアイコンが目印。これを新規作成する。
image.png

そして、custom vision用のプロジェクトを作ります。

https://docs.microsoft.com/ja-jp/learn/modules/classify-images-with-custom-vision-service/

プロジェクトを作成できたら、手順に沿ってラベル付け・学習を行っていきます。

手元で試す為には下記ページにurlとPrediction-keyをメモっておきます。
https://docs.microsoft.com/ja-jp/learn/modules/classify-images-with-custom-vision-service/5-call-the-prediction-endpoint-curl

3-2.応用サンプル

上記で学習したデータを使い、複数の写真のうち「最もピカソっぽい写真」を探す。
image.png

picasso.js
const axios = require('axios');

let pic = [
    { 
        name : "A",
        url : "https://www.cnn.co.jp/storage/2018/11/26/97e614a5a80cd9b784f4bdc82b4ec331/rembrandt-study-of-the-head-of-a-young-man.jpg" 
    },
    { 
        name : "B",
        url : "https://artmuseum.jpn.org/nakuonna.jpg" 
    },
    { 
        name : "C",
        url : "https://www.pakutaso.com/shared/img/thumb/MAX76_gjmax20140531_TP_V.jpg" 
    },
    ]

let items = [];
let name,prob,url;
let pic_len = pic.length;

const sendCustomVision = async (imageURL,picname) => {
  // If you have an image URL: にあるエンドポイントを使う
  const CUSTOM_VISION_API_ENDPOINT_URL = '{上記でメモったエンドポイント}';

  // Prediction-Keyの値を Prediction-Key ヘッダーに入れる
  // JSONで送るので Content-type ヘッダーに application/json 指定
  const configCustomVisionAPI = {
    url: CUSTOM_VISION_API_ENDPOINT_URL,
    method: 'post',
    headers: {
      'Content-type': 'application/json',
      'Prediction-Key':'{上記でメモったPrediction-Key}'
    },
    data: {
      url:imageURL
    }
  };

  // axiosの送信設定
  let responseCustomVision;
  try {
    // POSTリクエストで送る
    responseCustomVision = await axios.request(configCustomVisionAPI);
    //console.log("post OK");
    // データ送信が成功するとレスポンスが来る

    let len = responseCustomVision.data.predictions.length;
    var who = 'picasso';
    for(let i = 1; i <= len ; i++){
         // ピカソだけ取得
        const predictions = responseCustomVision.data.predictions[i];
        //console.log(responseCustomVision.data);

        if( predictions && predictions.tagName == who){  
           // console.log('tagName2: ' + predictions.tagName);

            name = picname;
            url = imageURL;
            prob = predictions.probability ;

            items.push({name:name,url:url,prob:prob });
        }
    }

    if(items.length == pic.length ){
        // 全ての画像のpicassoぽさを表示する
            console.log(items);

        // picassoぽさが最大の物だけを抽出  
            let items_len = items.length;
            let max_probability = 0;
            let max_name , max_url;
            for(let k=0; k<=items_len - 1;k++){
                if(max_probability < items[k].prob){
                    max_probability = items[k].prob;
                    max_name = items[k].name;
                    max_url = items[k].url;
                }
            }
        // 最もピカソっぽいものを表示
        console.log('最も'+ who + 'ぽい画像は' + max_name + ' : ' + max_url);  
    }

  } catch (error) {
    console.log("post Error");
    // ダメなときはエラー
    console.error(error);
  }
}

var j;
for(j =0; j <= pic_len - 1 ; j++){
    sendCustomVision(pic[j].url,pic[j].name);
}

結果。
Bがダントツでピカソっぽい。
image.png

判定したい画像を変えたり、変数whoのところを'pollock'や'rembrandt'に変えたりして遊べます。

Computer Vision API

4-0.参考

4-1.準備

基本的に上記参考ページに沿って進めていけばOKですが、ページ通りに進める為にはサンドボックスを使う必要があります。ページ内に書かれている通りやればできるはずなのでお忘れなく。

うまくいくと、右側に Azure cloud shellが表示されます。こんな感じ。
image.png

あとは基本的にページ内に記載されているコードをコピペで進めていけます。
アクセスキーはずっと使うので取得をお忘れなく。

4-2.手元で使うために

Learnページ内のサンドボックス環境で使うのも良いですが、自分の手元で動かすために、これまで同様リソースを追加します。
image.png

リソースを作成して「キーとエンドポイント」から「キー1」と「エンドポイント」をコピペしておきます。

エンドポイントに関しては、これまで
https://westus2.api.cognitive.microsoft.com/vision/v2.0/analyze?visualFeatures=Categories,Description&details=Landmarks

のように使っていた、https://westus2.api.cognitive.microsoft.com/ この部分を今取得したエンドポイントに入れ替えればOKです。

手元で動かすための基本の形

const axios = require('axios');

const ComputerVision = async (imageURL) => {
  // 
  const COMPUTER_VISION_API_ENDPOINT_URL = '{# 実行したい処理のエンドポイント}';
// 例:エンドポイント + vision/v2.0/analyze?visualFeatures=Categories,Description&details=Landmarks

  // Prediction-Keyの値を Prediction-Key ヘッダーに入れる
  // JSONで送るので Content-type ヘッダーに application/json 指定
  const configCustomVisionAPI = {
    url: COMPUTER_VISION_API_ENDPOINT_URL,
    method: 'post',
    headers: {
      'Content-type': 'application/json',
      'Ocp-Apim-Subscription-Key':'{# key1}' // key1
    },
    data: {
      url:imageURL
    }
  };

  // axiosの送信設定
  let response;
  try {
    // POSTリクエストで送る
    response = await axios.request(configCustomVisionAPI);
    //console.log("post OK");
    // データ送信が成功するとレスポンスが来る
    console.log(response.data);

  } catch (error) {
    console.log("post Error");
    // ダメなときはエラー
    console.error(error);
  }

}

var url = '{# 処理したい画像のURL}';
ComputerVision(url);

4-3.応用サンプル

上記の基本コード中の、
COMPUTER_VISION_API_ENDPOINT_URL と url 部分を変えることで色々と試せます。

4-3-1.アダルトコンテンツかどうか見分ける

変更箇所

  • COMPUTER_VISION_API_ENDPOINT_URL : 4-2のエンドポイント + /vision/v2.0/analyze?visualFeatures=Adult,Description

■しみけんさん
image.png

これはアダルトではないらしい。
image.png

■ベッドにいる外人さん
image.png

これはアダルトのようだ。
image.png

4-3-2.OCRで文字を読み取ってみる

変更箇所

  • COMPUTER_VISION_API_ENDPOINT_URL : 4-2のエンドポイント + /vision/v2.0/ocr?language=ja

■ みつをさん
image.png

-url : https://www.hankyu-hanshin-dept.co.jp/image/20150928aidamitsuoten03.jpg

NotDetected... 難易度が高すぎたようだ。
image.png

気を取り直して。

■エヴァの予告
image.png

何やら取れてるっぽい。
regionsの中のlinesにいるようだ。
image.png

さらに深いところにいた...
image.png

ということで、
console.log(response.data);

    for (let l = 0 ;  l <= response.data.regions[0].lines.length -1; l++){
        console.log(response.data.regions[0].lines[l].words);
    }

に変更して全てを取り出してみると、、、取れてる!!!
image.png

が、、、よく見ると。使徒の「使」が抜けてる。
「第壱話 徒、襲来」  惜しい!

終わりに

こんな手軽に、そしてタダで画像認識系の処理ができました。
便利な世の中です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

electron-react-boilerplateのpackage.jsonを理解する

electronのボイラープレートの中でもシェアが高く使い勝手の良いelectron-react-boilerplate
https://github.com/electron-react-boilerplate/electron-react-boilerplate

typescriptビルド環境からreact/redux react-routerなどのライブラリ群も入っていて尚且つテストツールやpre-commitまで入って至れり尽くせり。
そのまま使えばリッチな環境で開発が行えるが故に、定義ファイル群を理解せずに使いがち
あれこれディレクトリ構成を変えてるうちに動かなくなった なんてことにならないように一つ一つ読んで理解していこうと思う。

この記事ではpackage.jsonに書かれている定義から読み解いていく。

この記事で書かないこと

「electron-react-boilerplateの」package.jsonの機能的な部分だけに触れていくので以下のようなpackage.json自身のフィールドには触れない
repository, author contributors, license, bugs, keywords, homepage ...etc

electron-builderだとか、pre-commitなど拡張されたフィールドについて見ていく。

package.jsonに書かれているフィールド

package.jsonの定義を一つ一つ見ていく

lint-stagedフィールド

  "lint-staged": {
    "*.{js,jsx,ts,tsx}": [
      "cross-env NODE_ENV=development eslint --cache"
    ],
    "{*.json,.{babelrc,eslintrc,prettierrc,stylelintrc}}": [
      "prettier --ignore-path .eslintignore --parser json --write"
    ],
    "*.{css,scss}": [
      "stylelint --ignore-path .eslintignore --syntax scss --fix",
      "prettier --ignore-path .eslintignore --single-quote --write"
    ],
    "*.{html,md,yml}": [
      "prettier --ignore-path .eslintignore --single-quote --write"
    ]
  }

stageされたファイルを対象にlintをかけるlint-stagedに関する定義が書かれるフィールド
Readmeを参考に読み解いていく
https://github.com/okonet/lint-staged

1つ目
"*.{js,jsx,ts,tsx}": [
      "cross-env NODE_ENV=development eslint --cache"
    ],

gitにstageされたjs, jsx, ts, tsxファイルに対してeslintを実行する

2つ目
"{*.json,.{babelrc,eslintrc,prettierrc,stylelintrc}}": [
      "prettier --ignore-path .eslintignore --parser json --write"
    ],

.eslintignoreに記載されているものを除き、jsonと各rcファイルに対してprettierを実行する

3つ目
"*.{css,scss}": [
      "stylelint --ignore-path .eslintignore --syntax scss --fix",
      "prettier --ignore-path .eslintignore --single-quote --write"
    ],

.eslintignoreに記載されているものを除き、 css, scssファイルに対してstylelintとprettierを実行してファイルを修正する

4つ目
"*.{html,md,yml}": [
   "prettier --ignore-path .eslintignore --single-quote --write"
    ]

.eslintignoreに記載されているものを除き、 html, md, ymlファイルに対してprettierを実行してファイルを修正する

buildフィールド


"build": {
    "productName": "ElectronReact",
    "appId": "org.develar.ElectronReact",
    "files": [
      "dist/",
      "node_modules/",
      "app.html",
      "main.prod.js",
      "main.prod.js.map",
      "package.json"
    ],
    "dmg": {
      "contents": [
        {
          "x": 130,
          "y": 220
        },
        {
          "x": 410,
          "y": 220,
          "type": "link",
          "path": "/Applications"
        }
      ]
    },

electron-react-boilerplateが内包しているビルドモジュール electron-builderの定義が書かれているフィールド
公式ドキュメントを見ながら読み解いていく
https://www.electron.build/configuration/configuration

productName

アプリの名前 パッケージ化した時のファイル名になる。
スペースや特殊文字は使用不可とのこと

appId

MacではCFBundleIdentifier、WindowsではApplication User Model IDsとして使われる文字列
アプリケーションを識別するのに使う

files

packageのなかに含めるリソースファイルを定義する
ビルドファイルやnode_modulesなどが含まれる

directories
  • buildResources -> ビルド時に使用するリソースディレクトリ名 この中に入れたファイルがアプリに含まれると言うわけではなく、含めたいファイルはfilesフィールドで定義する必要があるらしい。

  • output -> パッケージ時の出力ディレクトリ名

dmg

Macのdmgファイルをどのように作成するかの定義
contents -> iconの表示位置などの設定 x,yはアイコンの中心位置への参照

win

windows用のビルドの設定項目
target -> ビルドで出力するファイルのタイプ

Linux

Linux用のビルドの設定項目
target -> ビルドで出力するファイルのタイプ

jestフィールド

"jest": {
    "testURL": "http://localhost/",
    "moduleNameMapper": {
      "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/internals/mocks/fileMock.js",
      "\\.(css|less|sass|scss)$": "identity-obj-proxy"
    },
    "moduleFileExtensions": [
      "js",
      "jsx",
      "ts",
      "tsx",
      "json"
    ],
    "moduleDirectories": [
      "node_modules",
      "app/node_modules"
    ],
    "setupFiles": [
      "./internals/scripts/CheckBuildsExist.js"
    ]
  },

テストランナーのjestの定義が書かれるフィールド
公式ドキュメントのConfigガイドを見ながら読み解いていく
https://jestjs.io/docs/ja/configuration

testURL

jsdom環境に与えられるURL defaultはabout:blankになっており、問題を起こすケースがあるのでlocalhostが入っている様子

moduleNameMapper

テストでは扱いづらい画像ファイル、フォントファイルなどのリソースをモックするための記述
画像、音声、動画ファイルなどは fileMock.js の内容でモックされる('test-file-stub'という文字列)
style関連のファイルはidentity-obj-proxyによってmockされる
https://github.com/keyz/identity-obj-proxy

moduleFileExtensions

使用するモジュールの拡張子を羅列する配列
公式ドキュメントによるとプロジェクトの中で一番頻度高く使用する拡張子を配列の最初に持ってくることが推奨されている。
typescriptを使用するならts, tsxを配列の最初に移動した方がテストのパフォーマンスが向上するだろう。

moduleDirectories

モジュールが格納されているディレクトリ名の配列を指定する。defaultはnode_modulesだが、app配下にも存在するので、app/node_modulesを追加した配列を指定している

setupFiles

各テストファイルの実行前に実行されるスクリプトを配列で指定する
ここで指定されているCheckBuildExists.jsでは main.prod.jsとrenderer.prod.jsの2つのファイルの存在チェックを行い、存在しなければ例外をThrowしてビルドコマンドを実行するメッセージを表示している

prettierフィールド

"prettier": {
    "overrides": [
      {
        "files": [".prettierrc", ".babelrc", ".eslintrc", ".stylelintrc"],
        "options": {
          "parser": "json"
        }
      }
    ],
    "singleQuote": true
  }

コードの自動整形をしてくれるPrettierの定義フィールド
公式のconfigガイドを見ていく
https://prettier.io/docs/en/configuration.html

overrides

特定のファイルに対して異なるオプションを設定するための定義
通常prettierはファイルの拡張子からパーサーを判断してくれるが、rcファイルは拡張子がないためjsonとしてフォーマットさせる定義が書いてある
この手法はprettier公式のexampleにも書いてある

singleQuote

ダブルクォートの代わりにシングルクォートを使用する

stylelintフィールド

  "stylelint": {
    "extends": ["stylelint-config-standard", "stylelint-config-prettier"]
  }

css, scssのフォーマットチェックをしてくれるstylelintの定義 少ないけれど公式をちゃんと見る
https://stylelint.io/user-guide/configure

extends

継承する既存の設定ファイルの配列 後に書いたファイルの設定が優先して適用される

  • stylelint-config-standard -> 60のルールを適用して推奨定義を拡張する
  • stylelint-config-prettier -> prettierと競合する不必要なルールを無効にする

renovate

  "renovate": {
    "extends": ["bliss"]
  }

npmパッケージの更新を補助するrenovateの定義フィールド
https://docs.renovatebot.com/configuration-options/#extends

extends

継承/使用するpresetを指定するファイル
指定されている'bliss'はrenovateの介入を最小限にする設定がされているらしい
https://github.com/amilajack/renovate-config-bliss

huskyフィールド

  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  }

gitの粗悪なcommitやpushを防いでくれるhuskyの定義フィールド
https://github.com/typicode/husky

hooks

何かを契機にコマンド実行するような定義をするフィールド
pre-commitはコミットの直前に動作させるコメントを定義できる
上述のlint-stagedを動作させている failした場合はcommitできない

終わりに

かなりボリュームはあったが、electronに限らずモダンWeb開発でよく使われるモジュールの定義が多数書かれていた。
ここに書かれている各モジュールについて理解することはelectron-react-boilerplateに限らず、多くの開発環境を作成、利用していくために役立つ知識になるでしょう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む