Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all 8902 articles
Browse latest View live

Node.js + osm-static-maps で OpenStreetMap や地理院地図の画像を取得する

$
0
0

概要

  • Node.js 用ライブラリの osm-static-maps を使って OpenStreetMap や地理院地図の画像を取得する

今回の環境

  • macOS Catalina + Node.js v14.9.0

osm-static-maps のインストール

osm-static-maps パッケージをインストールする。

$ npm install osm-static-maps

OpenStreetMap の地図画像を取得する

ソースコード。

'use strict'constosmsm=require('osm-static-maps');constfs=require('fs');(async()=>{try{// 地図画像の Buffer オブジェクトを取得constimageBinaryBuffer=awaitosmsm({width:800,// 画像の横幅(ピクセル)height:600,// 画像の縦幅(ピクセル)center:'136.882090,35.170560',// 経度,緯度zoom:20,// ズームレベルtype:'png'// PNG 画像フォーマット})// 地図画像データをファイルに出力awaitfs.promises.writeFile('osm.png',imageBinaryBuffer)process.exit(0);}catch(err){console.error(err);process.exit(1);}})();

実行結果。

osm.png

地理院地図の地図画像を取得する

ソースコード。

'use strict'constosmsm=require('osm-static-maps');constfs=require('fs');(async()=>{try{// 国土地理院の地理院タイルを使うconsttileserverUrl='https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png'constattribution='osm-static-maps / 出典: 地理院タイル'// 地図画像の Buffer オブジェクトを取得constimageBinaryBuffer=awaitosmsm({tileserverUrl:tileserverUrl,attribution:attribution,width:800,// 画像の横幅(ピクセル)height:600,// 画像の縦幅(ピクセル)center:'136.882090,35.170560',// 経度,緯度zoom:14,// ズームレベルtype:'png'// PNG 画像フォーマット})// 地図画像データをファイルに出力awaitfs.promises.writeFile('chiriin.png',imageBinaryBuffer)process.exit(0);}catch(err){console.error(err);process.exit(1);}})();

実行結果。

chiriin.png

参考資料


node環境でのmysql

$
0
0

nodejs環境でmysqlを使用する方法

mysqlインスタンスの作成

const mysql = require('mysql')
const connection = mysql.createPool({
     host: 'localhost',
     user: 'root',
     password: 'secret',
     database: 'my_db'
})

データの参照

select文

connection.query('SELECT * FROM `books` WHERE `author` = ?', ['David'], function(error, results, fields){
//error: クエリ実行時にエラーが発生したらエラーオブジェクトを持つ
//results: クエリの実行結果を持つ
//fields: クエリ実行結果のフィールドに関するオブジェクト
})

データの挿入

insert文

connection.query('INSERT INTO `books` SET ?', data, function(error, results, fields){
//error:クエリ実行時にエラーが発生したらエラーオブジェクトを持つ
//results:クエリの実行結果を持つ
//fields:クエリ実行結果のフィールドに関するオブジェクト
})

データの更新

update文

connection.query('UPDATE `books` SET カラム名 = ? WHERE id = ?, ['name', 3], function(error, results, field){
//error:クエリ実行時にエラーが発生したらエラーオブジェクトを持つ
//results:クエリの実行結果を持つ
//fields:クエリ実行結果のフィールドに関するオブジェクト
})

データの削除

delete文

connection.query('DELETE FROM `books` WHERE id = ?', [3], function(error, results, fields){
//error:クエリ実行時にエラーが発生したらエラーオブジェクトを持つ
//results:クエリの実行結果を持つ
//fields:クエリ実行結果のフィールドに関するオブジェクト
})

stdout is not a ttyに叱られる

$
0
0

完全に自分用の健忘録です

Git Bash(ver2.28)で実行しようとした際に、

$ node hogehuga.js 
stdout is not a tty

と叱られてしまった。悲しい。
原因はMinTTYのアレです。大体winptyを先頭につければ解決すると思ってたけど、今回は解決しなかった。
Gitを再インストールしてもダメ。

5分くらい悩んで、拡張子をつけたら動いた。

$ node.exe hogehuga.js

以下参考
https://github.com/mintty/mintty/wiki/Tips#inputoutput-interaction-with-alien-programs

クジラとネコの親子プログラミング - Docker for Windowsを使ってScratch3.0のオリジナル拡張機能を試してみよう。

$
0
0

親子プログラミングはGitとNode.jsが使えることが前提になっている?

小学校でのプログラミング教育の必須化に伴って、子供たちがScratch 3.0に触れる機会も増えるかと思います。そんな中、ブロックをくっつけるだけのお遊びツールだとあなどっていると、次のような連載が始まりました。

「親子でできる!Scratch と AWS を使った "ものづくり" 体験 - 1. 準備 〜 疎通確認編」
2020-09-01
How to be a Developer
金澤 圭

この連載の「2. 作業の前提について」で、衝撃的な次の一文があります。

PC に Git と Node.js がインストールされている必要があります。

保護者の方も、GitやNode.jsを当たり前のようにインストールしなければいけない時代になりましたね。でも、安心してください。Dockerという便利なツールがありますので、簡単に開発環境を整えることができます。

本記事では、Webベースの統合開発環境であるcode-serverとScratch 3.0 拡張機能の開発と実行に必要なソースコード(scratch-vm/scratch-gui)を詰め合わせたDockerコンテナ(jprad/s3coder)を使って、Windowsパソコン上で、Scratch 3.0 GUIサーバーを起動できるようにします。
後半では、「Scratch 3.0の拡張機能を作ってみよう」を参考にして、拡張機能を追加します。

拡張機能を追加して、Scratch 3.0 GUIでコーディング
image.png

必要なもの

Windows 10 パソコンにDockerをインストールしてください。Dockerのインストール方法は、リンク先や各種記事を参照してください。

セットアップと起動

Dockerイメージ(jprad/s3coder)を元にDockerコンテナを起動し、Scratch 3.0 GUIサーバーをWindowパソコンで実行できるようにします。

1. 共有フォルダの作成とDockerコンテナの初回起動

Windows 10 で、コマンドプロンプトを起動し、次の一連のコマンドを入力します。

コマンドプロンプト.cmd
cdc:\
mkdirscratch-ws
dockerrun--name s3coder-d -p 8080:8080 -p 8601:8601 ^
-v C:/scratch-ws:/home/coder/s3/scratch-ws -e PASSWORD=password^
jprad/s3coder/home/coder/s3REM docker run ... のコマンドが長いので「^(キャレット)」で改行しています。

C\scratch-wsフォルダは、Dockerコンテナにマウントされる共有フォルダです(/home/coder/s3/scratch-ws)。
docker runコマンドを開始すると、必要なDockerイメージ(合計:844.17 MB)のダウンロードが始まり、Dockerコンテナが起動されます(コンテナ名:s3coder)。

ダウンロード中のコマンドプロンプト
image.png

ダウンロード完了後、コンテナ起動済みのコマンドプロンプト
image.png

Dockerコンテナの初回起動時に、共有フォルダへのアクセスを許可するかどうかの確認通知が表示されますので、[Share it]で許可します。

共有を許可(Share it
image.png

2.code-severへのログインとScratch 3.0 GUIの起動

Dockerコンテナの起動完了後、code-server ( http://localhost:8080 )へアクセスし、ログインします(パスワードは、passwordです)。統合開発環境が開きますので、Dockerイメージに同梱されている scratch-guiを起動します。起動方法が構成ファイル(./s3/.vscode/launch.json)に設定されていますので、クリック操作のみで起動できます。scratch-guiの起動完了後、Scratch 3.0 GUI ( http://localhost:8601 )へアクセスすると、公式ページとは少しばかり異なっていますが、使い慣れたScratch 3.0でのコーディングが行えます。

手順
1. code-server ( http://localhost:8080 )へアクセスする。
2. パスワードにpasswordと入力し、[SUBMIT]ボタンをクリックし、ログインする。
3. [Run (Ctrl + Shift + D)]アイコンをクリックする。
4. ドロップダウンリストで'Run Scratch 3.0 GUI'(既定)を選択する。
5. [Start Debuging]アイコンをクリックする。
6. [TERMINAL]タブをクリックし、ビルド完了後の'Compiled successfully'が表示されるのを待つ。
7. Scratch 3.0 GUI ( http://localhost:8601 )へアクセスする。

scratch-guiの起動
image.png

Scratch 3.0 GUI
image.png

3.停止・再開

scratch-guiの再開・停止
scratch-guiを停止せずに再開するには、coder-serverの[Restart (Ctrl+Shift+F5)]アイコンをクリックします。
scratch-guiを停止するには、code-serverの[Stop (Shift+F5)]アイコンをクリックします。

scratch-guiの開始

  1. [Run (Ctrl + Shift + D)]アイコンをクリックする。
  2. ドロップダウンリストで'Run Scratch 3.0 GUI'(既定)を選択する。
  3. [Start Debuging]アイコンをクリックする。
  4. [TERMINAL]タブをクリックし、ビルド完了後の'Compiled successfully'が表示されるのを待つ。

Dockerコンテナの停止
コマンドプロンプトで次のコマンドを実行するとDockerコンテナ(コンテナ名:s3coder)が停止します。

コマンドプロンプト.cmd
dockerstops3coder

Dockerコンテナの再開
コマンドプロンプトで次のコマンドを実行するとDockerコンテナ(コンテナ名:s3coder)が再開します。

コマンドプロンプト.cmd
dockerstarts3coder

拡張機能の開発

Scratch 3.0 Extensions の開発に関する情報には、次のようなものがあります。

ここでは、「Scratch 3.0の拡張機能を作ってみよう」を参考にログを出力する拡張機能(ブロック)を実装(コピペ)してみます。

拡張機能に必要なファイル

拡張機能(s3coder_newblocks)を実装するには、次のファイルを追加・編集します。

scratch-vm

#フォルダファイル追加編集説明
1scratch-vm/src/extensions/s3coder_newblocksindex.js拡張機能(newblocks)の本体です。
2scratch-vm/src/extension-supportextension-manager.js追加した拡張機能(s3coder_newblocks)をbuiltinExtensionsに追加登録します。

scratch-gui

#フォルダファイル追加編集説明
1scratch-gui/src/lib/libraries/extensions/s3coder_newblocksnewblocks.png600x372ピクセルのPNG形式ファイルです。拡張機能の選択時にバナーとして表示されます。
2scratch-gui/src/lib/libraries/extensions/s3coder_newblocksnewblocks-small.png80x80ピクセルのPNG形式ファイルです。ブロックのアイコンとして表示されます。
3scratch-gui/src/lib/libraries/extensionsindex.jsPNG形式ファイルの読込定義と拡張ブロックの定義を設定します。

scratch-vm での実装

scratch-vmで、拡張機能そのものを実装します。

フォルダ作成

拡張機能用のフォルダを次の手順で作成します。

  1. code-serverのEXPLORERで、s3/scratch-vm/src/extensionsフォルダを選択します。
  2. 右クリックで、コンテキストメニューからNew Folderを選択し、's3coder_newblocks'フォルダを作成します。

index.jsファイルの実装

次の手順で、index.jsファイルを実装します。

  1. 作成した's3coder_newblocks'フォルダで右クリックし、New Fileを選択し、ファイル名をindex.jsとします。
  2. 「Scratch 3.0の拡張機能を作ってみよう/基本の書式 - 拡張機能の追加」からindex.jsのソースコードをコピー&ペーストします。

index.js
index.js
constArgumentType=require('../../extension-support/argument-type');constBlockType=require('../../extension-support/block-type');constCast=require('../../util/cast');constlog=require('../../util/log');/**
 * Icon svg to be displayed at the left edge of each extension block, encoded as a data URI.
 * @type {string}
 */// eslint-disable-next-line max-lenconstblockIconURI='data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgdmlld0JveD0iMCAwIDQwIDQwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj48ZyBpZD0iSUQwLjA4NjgyNDQzOTAwMDMzODMyIiB0cmFuc2Zvcm09Im1hdHJpeCgwLjQ5MTU0NjY2MDY2MTY5NzQsIDAsIDAsIDAuNDkxNTQ2NjYwNjYxNjk3NCwgLTY0LjUsIC03Ny4yNSkiPjxwYXRoIGlkPSJJRDAuNTcyMTQ2MjMwMzc3MjU2OSIgZmlsbD0iI0ZGOTQwMCIgc3Ryb2tlPSJub25lIiBkPSJNIDE4OCAxNDEgTCAyNTAgMTQxIEwgMjUwIDIwMyBMIDE4OCAyMDMgTCAxODggMTQxIFogIiB0cmFuc2Zvcm09Im1hdHJpeCgxLjI4NzkwMzMwODg2ODQwODIsIDAsIDAsIDEuMjg3OTAzMzA4ODY4NDA4MiwgLTExMC45LCAtMjQuNCkiLz48cGF0aCBpZD0iSUQwLjYzODMzNjEzNTA3NDQ5NjMiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI0ZGRkZGRiIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIGQ9Ik0gMTk2IDIwNCBDIDE5NiAyMDQgMTkyLjcwNiAxOTAuMDU4IDE5MyAxODMgQyAxOTMuMDc0IDE4MS4yMzYgMTk1Ljg4NiAxNzguNDU4IDE5NyAxODAgQyAyMDEuNDU1IDE4Ni4xNjggMjAzLjQ0MyAyMDMuNzU0IDIwNiAyMDEgQyAyMDkuMjExIDE5Ny41NDIgMjEwIDE2NiAyMTAgMTY2ICIgdHJhbnNmb3JtPSJtYXRyaXgoMSwgMCwgMCwgMSwgLTU3LCAxNS44KSIvPjxwYXRoIGlkPSJJRDAuNzU4NzMwMzU2NTgxNTA5MSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjRkZGRkZGIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgZD0iTSAyMTUgMTY5IEMgMjE1IDE2OSAyMTguMzY3IDE2OS41MzQgMjIwIDE3MCBDIDIyMC43MTYgMTcwLjIwNSAyMjEuMjc4IDE3MC44MTkgMjIyIDE3MSBDIDIyMi42NDYgMTcxLjE2MiAyMjMuMzY4IDE3MC43ODkgMjI0IDE3MSBDIDIyNC40NDcgMTcxLjE0OSAyMjUgMTcyIDIyNSAxNzIgIiB0cmFuc2Zvcm09Im1hdHJpeCgxLCAwLCAwLCAxLCAtNTcsIDE1LjgpIi8+PHBhdGggaWQ9IklEMC4yNDM2NzMwNzMxMjc4NjU4IiBmaWxsPSJub25lIiBzdHJva2U9IiNGRkZGRkYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBkPSJNIDIyNyAxNTQgQyAyMjcgMTU0IDIxOC41NTUgMTQ3Ljg5MCAyMTcgMTUxIEMgMjEyLjM0NSAxNjAuMzEwIDIxMS4yODkgMTcxLjczMyAyMTMgMTgyIEMgMjEzLjYxMiAxODUuNjcyIDIyMyAxODcgMjIzIDE4NyAiIHRyYW5zZm9ybT0ibWF0cml4KDEsIDAsIDAsIDEsIC01NywgMTUuOCkiLz48cGF0aCBpZD0iSUQwLjc5MzkzOTQ4MTk1NTAyMTYiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI0ZGRkZGRiIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIGQ9Ik0gMTc1IDIwMC41MDAgQyAxNzUgMjAwLjUwMCAxNjkuODA1IDIyMS45MTMgMTcxIDIyMi43NTAgQyAxNzIuMTk1IDIyMy41ODcgMTc4Ljc5NSAyMDUuMjk1IDE4Mi41MDAgMjA1Ljc1MCBDIDE4NS45MjAgMjA2LjE3MCAxODEuODU5IDIyNC41MDAgMTg1LjI1MCAyMjQuNTAwIEMgMTg5LjIxMyAyMjQuNTAwIDE5Ny4yNTAgMjA1Ljc1MCAxOTcuMjUwIDIwNS43NTAgIi8+PC9nPjwvc3ZnPg==';/**
 * Icon svg to be displayed in the category menu, encoded as a data URI.
 * @type {string}
 */// eslint-disable-next-line max-lenconstmenuIconURI='data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgdmlld0JveD0iMCAwIDQwIDQwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj48ZyBpZD0iSUQwLjA4NjgyNDQzOTAwMDMzODMyIiB0cmFuc2Zvcm09Im1hdHJpeCgwLjQ5MTU0NjY2MDY2MTY5NzQsIDAsIDAsIDAuNDkxNTQ2NjYwNjYxNjk3NCwgLTY0LjUsIC03Ny4yNSkiPjxwYXRoIGlkPSJJRDAuNTcyMTQ2MjMwMzc3MjU2OSIgZmlsbD0iI0ZGOTQwMCIgc3Ryb2tlPSJub25lIiBkPSJNIDE4OCAxNDEgTCAyNTAgMTQxIEwgMjUwIDIwMyBMIDE4OCAyMDMgTCAxODggMTQxIFogIiB0cmFuc2Zvcm09Im1hdHJpeCgxLjI4NzkwMzMwODg2ODQwODIsIDAsIDAsIDEuMjg3OTAzMzA4ODY4NDA4MiwgLTExMC45LCAtMjQuNCkiLz48cGF0aCBpZD0iSUQwLjYzODMzNjEzNTA3NDQ5NjMiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI0ZGRkZGRiIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIGQ9Ik0gMTk2IDIwNCBDIDE5NiAyMDQgMTkyLjcwNiAxOTAuMDU4IDE5MyAxODMgQyAxOTMuMDc0IDE4MS4yMzYgMTk1Ljg4NiAxNzguNDU4IDE5NyAxODAgQyAyMDEuNDU1IDE4Ni4xNjggMjAzLjQ0MyAyMDMuNzU0IDIwNiAyMDEgQyAyMDkuMjExIDE5Ny41NDIgMjEwIDE2NiAyMTAgMTY2ICIgdHJhbnNmb3JtPSJtYXRyaXgoMSwgMCwgMCwgMSwgLTU3LCAxNS44KSIvPjxwYXRoIGlkPSJJRDAuNzU4NzMwMzU2NTgxNTA5MSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjRkZGRkZGIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgZD0iTSAyMTUgMTY5IEMgMjE1IDE2OSAyMTguMzY3IDE2OS41MzQgMjIwIDE3MCBDIDIyMC43MTYgMTcwLjIwNSAyMjEuMjc4IDE3MC44MTkgMjIyIDE3MSBDIDIyMi42NDYgMTcxLjE2MiAyMjMuMzY4IDE3MC43ODkgMjI0IDE3MSBDIDIyNC40NDcgMTcxLjE0OSAyMjUgMTcyIDIyNSAxNzIgIiB0cmFuc2Zvcm09Im1hdHJpeCgxLCAwLCAwLCAxLCAtNTcsIDE1LjgpIi8+PHBhdGggaWQ9IklEMC4yNDM2NzMwNzMxMjc4NjU4IiBmaWxsPSJub25lIiBzdHJva2U9IiNGRkZGRkYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBkPSJNIDIyNyAxNTQgQyAyMjcgMTU0IDIxOC41NTUgMTQ3Ljg5MCAyMTcgMTUxIEMgMjEyLjM0NSAxNjAuMzEwIDIxMS4yODkgMTcxLjczMyAyMTMgMTgyIEMgMjEzLjYxMiAxODUuNjcyIDIyMyAxODcgMjIzIDE4NyAiIHRyYW5zZm9ybT0ibWF0cml4KDEsIDAsIDAsIDEsIC01NywgMTUuOCkiLz48cGF0aCBpZD0iSUQwLjc5MzkzOTQ4MTk1NTAyMTYiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI0ZGRkZGRiIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIGQ9Ik0gMTc1IDIwMC41MDAgQyAxNzUgMjAwLjUwMCAxNjkuODA1IDIyMS45MTMgMTcxIDIyMi43NTAgQyAxNzIuMTk1IDIyMy41ODcgMTc4Ljc5NSAyMDUuMjk1IDE4Mi41MDAgMjA1Ljc1MCBDIDE4NS45MjAgMjA2LjE3MCAxODEuODU5IDIyNC41MDAgMTg1LjI1MCAyMjQuNTAwIEMgMTg5LjIxMyAyMjQuNTAwIDE5Ny4yNTAgMjA1Ljc1MCAxOTcuMjUwIDIwNS43NTAgIi8+PC9nPjwvc3ZnPg==';/**
 * Class for the new blocks in Scratch 3.0
 * @param {Runtime} runtime - the runtime instantiating this block package.
 * @constructor
 */classScratch3NewBlocks{constructor(runtime){/**
         * The runtime instantiating this block package.
         * @type {Runtime}
         */this.runtime=runtime;//this._onTargetCreated = this._onTargetCreated.bind(this);//this.runtime.on('targetWasCreated', this._onTargetCreated);}/**
     * @returns {object} metadata for this extension and its blocks.
     */getInfo(){return{id:'newblocks',name:'New Blocks',menuIconURI:menuIconURI,blockIconURI:blockIconURI,blocks:[{opcode:'writeLog',blockType:BlockType.COMMAND,text:'log [TEXT]',arguments:{TEXT:{type:ArgumentType.STRING,defaultValue:"hello"}}},{opcode:'getBrowser',text:'browser',blockType:BlockType.REPORTER}],menus:{}};}/**
     * Write log.
     * @param {object} args - the block arguments.
     * @property {number} TEXT - the text.
     */writeLog(args){consttext=Cast.toString(args.TEXT);log.log(text);}/**
     * Get the browser.
     * @return {number} - the user agent.
     */getBrowser(){returnnavigator.userAgent;}}module.exports=Scratch3NewBlocks;

image.png

extension-manager.jsファイルの変更

scratch-vm/src/extension-supportフォルダのextension-manager.jsファイルを開き、builtinExtensionsに、次のように拡張機能(s3coder_newblocks)を追加登録します。
※カンマで区切ることを忘れないでください。

extension-manager.js
constbuiltinExtensions={...gdxfor:()=>require('../extensions/scratch3_gdx_for'),newblocks:()=>require('../extensions/s3coder_newblocks'),};

image.png

scratch-gui での設定

scratch-guiで、拡張機能を選択できるように設定します。

フォルダ作成

画像保存用のフォルダを次の手順で作成します。

  1. code-serverのEXPLORERで、s3/scratch-gui/src/lib/libraries/extensionsフォルダを選択します。
  2. 右クリックで、コンテキストメニューからNew Folderを選択し、's3coder_newblocks'フォルダを作成します。

画像ファイル

画像ファイルをダウンロードし、作成したフォルダへ保存します。

2つの画像ファイル(クリックして展開)

newblocks.png
image.png

newblocks-small.png
image.png

  1. 2つの画像ファイルをダウンロードし、C:\scratch-wsフォルダへ保存します。
  2. code-serverのEXPLORERで、s3/scratch-wsフォルダを開き、2つの画像ファイルを選択し、右クリックでコンテキストメニューからCopyします。
  3. s3/scratch-gui/src/lib/libraries/extensions/s3coder_newblocksフォルダを選択し、右クリックでコンテキストメニューからPasteします。

image.png

index.jsファイルの設定

s3/scratch-gui/src/lib/libraries/extensionsフォルダのindex.jsファイルを開き、次のように拡張機能の設定を行います。これにより、Scratch 3.0 GUIの拡張機能の追加で、拡張機能を選択できるようになります。

index.js
...importgdxforConnectionSmallIconURLfrom'./gdxfor/gdxfor-small.svg';importnewblocksImagefrom'./s3coder_newblocks/newblocks.png';importnewblocksInsetImagefrom'./s3coder_newblocks/newblocks-small.png';exportdefault[...),helpLink:'https://scratch.mit.edu/vernier'},{name:(<FormattedMessagedefaultMessage="New Blocks"description="Name for the 'New Blocks' extension"id="gui.extension.newblocks.name"/>),extensionId:'newblocks',iconURL:newblocksImage,insetIconURL:newblocksInsetImage,description:(<FormattedMessagedefaultMessage="New extension"description="Description for the 'New Blocks' extension"id="gui.extension.newblocks.description"/>),featured:true},];

image.png

動作確認

scratch-guiを開始し、Scratch 3.0 GUIを開きなおすと、「拡張機能を追加」で、新しい拡張機能を選択できます。
image.png

logブロックを実行すると、ブラウザのConsoleにhelloと出力されます。

image.png

おわりに

【メモ】Dockerイメージの作成
dos.cmd
cdc:\
mkdirscratch-ws
dockerrun-it -p 8080:8080 -p 8601:8601 -v C:/scratch-ws:/home/coder/s3/scratch-ws -e PASSWORD=passwordcodercom/code-server /home/coder/s3

http://localhost:8080/へアクセス

sudo apt-get update -ysudo apt-get install-y nodejs npm
sudo npm install-g npm
sudo chown-R coder:coder /home/coder/s3
git clone --depth 1 https://github.com/llk/scratch-vm.git
git clone --depth 1 https://github.com/llk/scratch-gui.git

cd scratch-vm
npm ci
sudo npm link

cd ../scratch-gui
npm ci
sudo npm link scratch-vm

npm start
--->> [Ctrl]+[C]で中断

http://localhost:8601/へアクセス

  • scratch-guiのポート
    ENV環境変数で設定可能

Dockerfileの記述

docker build -t test-image .

docker run --name test-container -it -p 8080:8080 -p 8601:8601 -v C:/scratch-ws:/home/coder/s3/scratch-ws -e PASSWORD=password test-image /home/coder/s3

Reactでnpm startができなくなった話。

$
0
0

React.js Node.js環境でnpm start実行時にエラー発生。

解決策だけ見たい人は最後に載せてます。(予定)

問題編

React.js Node.js環境でnpm startできなくなった。

経緯編

create-react-appでreactのアプリを作った。

app.jsを編集し、いざ起動する。

npm start 

そうすると、以下のようなエラーが発生。

> cycle@0.1.0 start /Users/user/Documents/ubuntu/cycle
> react-scripts start


There might be a problem with the project dependency tree.
It is likely not a bug in Create React App, but something you need to fix locally.

The react-scripts package provided by Create React App requires a dependency:

  "webpack": "4.42.0"

Don't try to install it manually: your package manager does it automatically.
However, a different version of webpack was detected higher up in the tree:

  /Users/user/Documents/ubuntu/node_modules/webpack (version: 4.44.1) 

Manually installing incompatible versions is known to cause hard-to-debug issues.

If you would prefer to ignore this check, add SKIP_PREFLIGHT_CHECK=true to an .env file in your project.
That will permanently disable this message but you might encounter other issues.

To fix the dependency tree, try following the steps below in the exact order:

  1. Delete package-lock.json (not package.json!) and/or yarn.lock in your project folder.
  2. Delete node_modules in your project folder.
  3. Remove "webpack" from dependencies and/or devDependencies in the package.json file in your project folder.
  4. Run npm install or yarn, depending on the package manager you use.

In most cases, this should be enough to fix the problem.
If this has not helped, there are a few other things you can try:

  5. If you used npm, install yarn (http://yarnpkg.com/) and repeat the above steps with it instead.
     This may help because npm has known issues with package hoisting which may get resolved in future versions.

  6. Check if /Users/user/Documents/ubuntu/node_modules/webpack is outside your project directory.
     For example, you might have accidentally installed something in your home folder.

  7. Try running npm ls webpack in your project folder.
     This will tell you which other package (apart from the expected react-scripts) installed webpack.

If nothing else helps, add SKIP_PREFLIGHT_CHECK=true to an .env file in your project.
That would permanently disable this preflight check in case you want to proceed anyway.

P.S. We know this message is long but please read the steps above :-) We hope you find them helpful!

npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! cycle@0.1.0 start: `react-scripts start`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the cycle@0.1.0 start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/user/.npm/_logs/2020-09-07T13_28_49_449Z-debug.log
(base) user-no-MacBook-Air:cycle user$ SKIP_PREFLIGHT_CHECK=true npm start

> cycle@0.1.0 start /Users/user/Documents/ubuntu/cycle
> react-scripts start

Attempting to bind to HOST environment variable: x86_64-apple-darwin13.4.0
If this was unintentional, check that you haven't mistakenly set it in your shell.
Learn more here: https://bit.ly/CRA-advanced-config

dyld: lazy symbol binding failed: Symbol not found: _FSEventStreamCreate
  Referenced from: /Users/user/Documents/ubuntu/cycle/node_modules/webpack-dev-server/node_modules/fsevents/build/Release/fse.node
  Expected in: flat namespace

dyld: Symbol not found: _FSEventStreamCreate
  Referenced from: /Users/user/Documents/ubuntu/cycle/node_modules/webpack-dev-server/node_modules/fsevents/build/Release/fse.node
  Expected in: flat namespace

npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! cycle@0.1.0 start: `react-scripts start`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the cycle@0.1.0 start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/user/.npm/_logs/2020-09-07T13_29_11_414Z-debug.log
(base) user-no-MacBook-Air:cycle user$ eact-scripts start
-bash: eact-scripts: command not found
(base) user-no-MacBook-Air:cycle user$ react-scripts start

There might be a problem with the project dependency tree.
It is likely not a bug in Create React App, but something you need to fix locally.

The react-scripts package provided by Create React App requires a dependency:

  "webpack": "4.42.0"

Don't try to install it manually: your package manager does it automatically.
However, a different version of webpack was detected higher up in the tree:

  /Users/user/Documents/ubuntu/node_modules/webpack (version: 4.44.1) 

Manually installing incompatible versions is known to cause hard-to-debug issues.

If you would prefer to ignore this check, add SKIP_PREFLIGHT_CHECK=true to an .env file in your project.
That will permanently disable this message but you might encounter other issues.

To fix the dependency tree, try following the steps below in the exact order:

  1. Delete package-lock.json (not package.json!) and/or yarn.lock in your project folder.
  2. Delete node_modules in your project folder.
  3. Remove "webpack" from dependencies and/or devDependencies in the package.json file in your project folder.
  4. Run npm install or yarn, depending on the package manager you use.

In most cases, this should be enough to fix the problem.
If this has not helped, there are a few other things you can try:

  5. If you used npm, install yarn (http://yarnpkg.com/) and repeat the above steps with it instead.
     This may help because npm has known issues with package hoisting which may get resolved in future versions.

  6. Check if /Users/user/Documents/ubuntu/node_modules/webpack is outside your project directory.
     For example, you might have accidentally installed something in your home folder.

  7. Try running npm ls webpack in your project folder.
     This will tell you which other package (apart from the expected react-scripts) installed webpack.

If nothing else helps, add SKIP_PREFLIGHT_CHECK=true to an .env file in your project.
That would permanently disable this preflight check in case you want to proceed anyway.

P.S. We know this message is long but please read the steps above :-) We hope you find them helpful!

検索すると、

SKIP_PREFLIGHT_CHECK=true

を.envファイルに記載すると良いと出てきた。(勘違いかも。。)

だが、まずcreate-react-appで作ったアプリのフォルダ内に.envファイルが見つからない。。。

どうしようと悩み、調べ続けるとnpm startの後に、直接打てば良いのでは? との記事を発見。。

試してみる。

SKIP_PREFLIGHT_CHECK=true npm start

結果。

> cycle@0.1.0 start /Users/user/Documents/ubuntu/cycle
> react-scripts start

Attempting to bind to HOST environment variable: x86_64-apple-darwin13.4.0
If this was unintentional, check that you haven't mistakenly set it in your shell.
Learn more here: https://bit.ly/CRA-advanced-config

dyld: lazy symbol binding failed: Symbol not found: _FSEventStreamCreate
  Referenced from: /Users/user/Documents/ubuntu/cycle/node_modules/webpack-dev-server/node_modules/fsevents/build/Release/fse.node
  Expected in: flat namespace

dyld: Symbol not found: _FSEventStreamCreate
  Referenced from: /Users/user/Documents/ubuntu/cycle/node_modules/webpack-dev-server/node_modules/fsevents/build/Release/fse.node
  Expected in: flat namespace

npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! cycle@0.1.0 start: `react-scripts start`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the cycle@0.1.0 start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/user/.npm/_logs/2020-09-07T13_29_11_414Z-debug.log
(base) user-no-MacBook-Air:cycle user$ eact-scripts start
-bash: eact-scripts: command not found
(base) user-no-MacBook-Air:cycle user$ react-scripts start

There might be a problem with the project dependency tree.
It is likely not a bug in Create React App, but something you need to fix locally.

The react-scripts package provided by Create React App requires a dependency:

  "webpack": "4.42.0"

Don't try to install it manually: your package manager does it automatically.
However, a different version of webpack was detected higher up in the tree:

  /Users/user/Documents/ubuntu/node_modules/webpack (version: 4.44.1) 

Manually installing incompatible versions is known to cause hard-to-debug issues.

If you would prefer to ignore this check, add SKIP_PREFLIGHT_CHECK=true to an .env file in your project.
That will permanently disable this message but you might encounter other issues.

To fix the dependency tree, try following the steps below in the exact order:

  1. Delete package-lock.json (not package.json!) and/or yarn.lock in your project folder.
  2. Delete node_modules in your project folder.
  3. Remove "webpack" from dependencies and/or devDependencies in the package.json file in your project folder.
  4. Run npm install or yarn, depending on the package manager you use.

In most cases, this should be enough to fix the problem.
If this has not helped, there are a few other things you can try:

  5. If you used npm, install yarn (http://yarnpkg.com/) and repeat the above steps with it instead.
     This may help because npm has known issues with package hoisting which may get resolved in future versions.

  6. Check if /Users/user/Documents/ubuntu/node_modules/webpack is outside your project directory.
     For example, you might have accidentally installed something in your home folder.

  7. Try running npm ls webpack in your project folder.
     This will tell you which other package (apart from the expected react-scripts) installed webpack.

If nothing else helps, add SKIP_PREFLIGHT_CHECK=true to an .env file in your project.
That would permanently disable this preflight check in case you want to proceed anyway.

P.S. We know this message is long but please read the steps above :-) We hope you find them helpful!

んーうまくいかない。。。。

さらに調べる。

そうすると、今度はホームディレクトリのnode_modulesを全削除すればうまくいくとの記事を発見。

試してみる。

create-react-appで作った該当node_modulesのみ、一時名前を変えて退避。

そして、それ以外のnode_modulesをホームに戻って全削除。

以下全削除のcommand

find . -name 'node_modules' -type d -prune -exec rm -rf '{}' +

そして、create-react-appで作ったアプリのディレクトリに戻って、npm startを再度実行。

またエラーになった。

でもエラー文は変わった。

> cycle@0.1.0 start /Users/user/Documents/ubuntu/cycle
> react-scripts start

internal/modules/cjs/loader.js:895
  throw err;
  ^

Error: Cannot find module 'entities/maps/entities.json'
Require stack:
- /Users/user/Documents/ubuntu/cycle/node_modules/htmlparser2/lib/Tokenizer.js
- /Users/user/Documents/ubuntu/cycle/node_modules/htmlparser2/lib/Parser.js
- /Users/user/Documents/ubuntu/cycle/node_modules/htmlparser2/lib/index.js
- /Users/user/Documents/ubuntu/cycle/node_modules/renderkid/lib/tools.js
- /Users/user/Documents/ubuntu/cycle/node_modules/renderkid/lib/renderKid/styleApplier/inline.js
- /Users/user/Documents/ubuntu/cycle/node_modules/renderkid/lib/RenderKid.js
- /Users/user/Documents/ubuntu/cycle/node_modules/pretty-error/lib/PrettyError.js
- /Users/user/Documents/ubuntu/cycle/node_modules/html-webpack-plugin/lib/errors.js
- /Users/user/Documents/ubuntu/cycle/node_modules/html-webpack-plugin/index.js
- /Users/user/Documents/ubuntu/cycle/node_modules/react-scripts/config/webpack.config.js
- /Users/user/Documents/ubuntu/cycle/node_modules/react-scripts/scripts/start.js
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:892:15)
    at Function.Module._load (internal/modules/cjs/loader.js:742:27)
    at Module.require (internal/modules/cjs/loader.js:964:19)
    at require (internal/modules/cjs/helpers.js:88:18)
    at Object.<anonymous> (/Users/user/Documents/ubuntu/cycle/node_modules/htmlparser2/lib/Tokenizer.js:4:17)
    at Module._compile (internal/modules/cjs/loader.js:1075:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1096:10)
    at Module.load (internal/modules/cjs/loader.js:940:32)
    at Function.Module._load (internal/modules/cjs/loader.js:781:14)
    at Module.require (internal/modules/cjs/loader.js:964:19) {
  code: 'MODULE_NOT_FOUND',
  requireStack: [
    '/Users/user/Documents/ubuntu/cycle/node_modules/htmlparser2/lib/Tokenizer.js',
    '/Users/user/Documents/ubuntu/cycle/node_modules/htmlparser2/lib/Parser.js',
    '/Users/user/Documents/ubuntu/cycle/node_modules/htmlparser2/lib/index.js',
    '/Users/user/Documents/ubuntu/cycle/node_modules/renderkid/lib/tools.js',
    '/Users/user/Documents/ubuntu/cycle/node_modules/renderkid/lib/renderKid/styleApplier/inline.js',
    '/Users/user/Documents/ubuntu/cycle/node_modules/renderkid/lib/RenderKid.js',
    '/Users/user/Documents/ubuntu/cycle/node_modules/pretty-error/lib/PrettyError.js',
    '/Users/user/Documents/ubuntu/cycle/node_modules/html-webpack-plugin/lib/errors.js',
    '/Users/user/Documents/ubuntu/cycle/node_modules/html-webpack-plugin/index.js',
    '/Users/user/Documents/ubuntu/cycle/node_modules/react-scripts/config/webpack.config.js',
    '/Users/user/Documents/ubuntu/cycle/node_modules/react-scripts/scripts/start.js'
  ]
}
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! cycle@0.1.0 start: `react-scripts start`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the cycle@0.1.0 start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/user/.npm/_logs/2020-09-07T13_38_20_165Z-debug.log

→今ここ。。。

解決し次第追記する。

解決編

まだ未解決。

参考文献

https://teratail.com/questions/177560
https://qiita.com/yuta0801/items/118d9478ad536a443f3f
https://mebee.info/2020/03/03/post-6791/

AWSLambda(Node.js)の入力パラメータチェック(バリデーション)

$
0
0

AWSLambda(Node.js)のREST APIにおいて入力パラメータのチェックがしたい
そうだ、value-schemaを使おう

value-schemaができること

1.必須パラメータの有無、
2.型、
3.想定範囲内に存在するか(e.g.limit < input < limit)、
上記3点における入力パラメータの検証と修正ができる

使い方

大きく2つの書き方がある....と勝手に思ってる
基本のフォーマットは vs.型名( {オプション} )

//====その1. オブジェクトの中身をまとめてチェックする方法====varvs=require("value-schema");// 入力パラメータのオブジェクトletrequestbody={testId:"test",testNum:1,testlist:[1,2,3],NoCheckid:"no",};// 入力パラメータの満たすべき条件constschemaObject={testId:vs.string({minLength:3}),//eventIdが文字列かつ5文字以上testNum:vs.number({minValue:1,}),//testNumが数字かつ1以上testlist:vs.array({minLength:2}),//testlistがarrayかつ2要素以上//noNoCheckidは条件を書いてないのでチェックされない};// 全ての条件を満たさない場合エラーを投げる,満たす場合は変数にいれるconstcheckedRequestbody=vs.applySchemaObject(schemaObject,requestbody,(err)=>{thrownewError("入力パラメータがおかしい")});
//====その2. パラメータ単体でチェックする方法====varvs=require("value-schema");constflag=true;constcheckedName=vs.boolean().applyTo(flag,(err)=>{//flagがbool値であるthrownewError("入力flagがおかしい")//条件を見たさない場合err投げる});

ほぼ全ての型で使える便利なオプション

{strictType: true} : 厳密なタイプチェックができる、デフォルトはオフ
{ifUndefined: somevalue} : パラメータがundefinedの場合に任意の値を入れておく
{ifNull: somevalue} : パラメータがnullの場合に任意の値を入れておく
{ifEmptyString: somevalue} : パラメータが空文字""の場合に任意の値を入れておく

*その他オプションなどは公式を参照

Node.jsのHTTPクライアントまとめ2020

$
0
0

概要

2020/2にNode.jsのrequestモジュールがDeprecated(非推奨)になってしまいました。
https://qiita.com/kE-sakai/items/1a64462ad41c36813c92

そこで複数の代替モジュールについて各種比較サイトを調べてまとめました。

モジュール名優位性[参照サイトNo]
node-fetchバンドルサイズが小さい[1]
ダウンロード数が多い[2]
axiosGitHub starsが多い[3]
bentAPIスタイルが多い[1]
アップデートが頻繁[5]
superagentメンテナーが多い[5]
kyインストールサイズが小さい[3]
got
make-fetch-happen
unfetch
tiny-json-http
needle
urllib

参照サイト

1. Alternative libraries to request

https://github.com/request/request/issues/3143
スレッド形式。モジュール数が多い。

2. npm trends

https://www.npmtrends.com/node-fetch-vs-bent-vs-got-vs-make-fetch-happen-vs-axios-vs-superagent-vs-fetch-vs-unfetch-vs-tiny-json-http-vs-needle
グラフ形式。ダウンロード数が時系列で見れる。

3. got#comparison

https://github.com/sindresorhus/got#comparison
表形式。各種特徴について細かく比較されている。

4. Request is going into maintenance mode, this is what you need to know

https://nodesource.com/blog/express-going-into-maintenance-mode
文章形式。使い方や特徴が書いてある。

5. NPMCompare

https://npmcompare.com/compare/axios,bent,got,node-fetch,superagent
表形式。総合的に評価して全体ポイントを出している。

まとめ

総合的には参照サイト5のように、node-fetchが一番優位性があるように感じました。

Microsoft Teamsにメッセージと画像を送る(Node.js)

$
0
0

Teamsのチャンネルに投稿用アカウント(Incoming Webhook)を追加して、
チャンネルにメッセージや画像を投稿する

1.投稿したいチャンネルにIncoming Webhookを追加

  • Teamsの投稿したいチャンネルで右クリックして [コネクタ] を選択
  • [incoming Webhook] を検索して [追加] をクリック、次画面で再度 [追加] をクリック
  • incoming Webhookの名前とアイコン画像を設定して [作成] をクリック
  • Webhook用のURLが表示されるのでコピーして [完了] をクリック

2.base64にデコードした画像をメッセージに埋め込んでpostで送信

  • node.jsの環境つくり、"fs"と"request"をインストール
  • index.jsと同じ階層に画像を準備
  • 以下のindex.jsを実行
index.js
constfs=require('fs');constrequest=require('request');fs.readFile('画像名.png','base64',(date)=>{letbase64date=date;letmessage="魔法陣ぐるぐる"//画像の上に書きたいメッセージletoptions={uri:"teamsからコピーしたURL",headers:{"Content-type":"application/json",},json:{"text":message+"<br>"+"![]"+"("+base64date+")"}};request.post(options);// postリクエスト送信});


こんな感じで送られる
image.png

注意

  • プライベートチャンネルにはこの方法で送信できない
  • 画像サイズが大きすぎると、base64にエンコードしたときに文字列が長過ぎて送れない

AWS S3 アクセスログの集約化

$
0
0

前提

AWSのS3のアクセスログは1回のアクセスにつき、指定したバケットに1個のアクセスログファイルを作成するとします。

感覚的には1回につき1個のファイルではなく、同時間に発行されたログを1つのファイルにまとめて吐いているようです。細かいことはAmazon S3 サーバーアクセスのログ記録を見て下さい。

つまり「1時間毎に1回、1日で計24回のアクセスがあったとすると作成されるログファイルの数は24個」としてこの記事を読んで下さい。

何がしたいか

前提で述べたように1回のアクセスで1個のログファイルが作成されるので、1000回アクセスされると1000個のログファイルが作成されてしまいます。「1日に発行されたログを全てまとめて見たい」という時に全てのログファイルをローカルにダウンロードするなんて事はしたくありません。S3の仕様上、それなりにコストがかかります。よってこれらのログファイルを1日毎にまとめてしまいます。1日毎ではなく1週間毎や1ヶ月毎にまとめてしまってもいいかもしれません。ただこの後に使用するAWSのLambdaの仕様上ファイルが大きくなりすぎると無理かもしれないので1日毎にしました。

Direction

SequenceOfLogManagement.png

上の図のような流れで処理を実行します。

1. 左から「User」が「Bucket A」にアクセスする(2020-09-07-14-30)。

2. 「Bucket A」はアクセスされるとログを「Bucket B」に書き込む。

3. 「Bucket B」はログを書き込まれたら「Bucket C」においてある、「2020_09_07.txt」にそのログを追記する。

これらの処理の内、1と2の処理はAmazon S3 サーバーアクセスのログ記録の設定で自動で行われるので割愛します。

前準備

3の処理をするためにLambdaより関数を作成します。今回私はNode.jsのデプロイパッケージを作成することで関数を作成しました。

Node.js の Lambda デプロイパッケージを作成するには、どうすればよいですか?

作成する際には、アクセス権限に「S3FullAccess」をもつロールを与えておいて下さい。

作成した関数の名前を「sum_logs」とします。sum_logsの設定、「トリガーを追加」より、「Bucket B」に新しいオブジェクトが作成されたらsum_logsが実行されるようにしておきます。

また「Bucket C」に「2020_09_07.txt」というファイルを準備しておいて下さい。

「Bucket B」や「Bucket C」と書いていますが実際のBucketの名前は"B"や"C"として以下のコードを扱って下さい。

index.js

var AWS = require("aws-sdk");
const moment = require("moment");
const s3 = new AWS.S3({
  region: "ap-northeast-1",
});

exports.handler = async (event, context, callback) => {
  try {
    //this means Bucket "C"
    const dest_bucket = "C"; 

    var uploaded_params = {
      // event.Records[0].s3.bucket.name means Bucket "B"
      Bucket: event.Records[0].s3.bucket.name,
      Key: event.Records[0].s3.object.key,
    };
    var uploaded_obj = await s3.getObject(uploaded_params).promise();
    var uploaded_body = uploaded_obj.Body.toString();

    var dest_params = {
      Bucket: dest_bucket,
      Key: moment().format("YYYY-MM-DD") + ".txt",
    };
    var dest_obj = await s3.getObject(dest_params).promise();
    var dest_body = dest_obj.Body.toString();
    dest_body += uploaded_body;

    var new_params = {
      Bucket: dest_bucket,
      Key: moment().format("YYYY-MM-DD") + ".txt",
      Body: dest_body,
    };
    var put_obj = await s3.putObject(new_params).promise();

    // delete uploaded logs in Bucket "B"
    var deleted = await s3.deleteObject(uploaded_params).promise();

    return;
  } catch (e) {
    console.log(e);
    return;
  }
};

解説

内容は大体読めば分かるでしょう。
今回デプロイパッケージにしたのはMoment.jsを使用したかったからです。個人的には扱いやすいのでおすすめします。

注意点とまとめ

今回、日付毎にファイルをまとめていますが、そのベースとなるファイル(今回は2020_09_07.txt)は前もって特定のバケット(今回はBucket "C")に作成しておく必要があります。

このベースになるファイルも手動で作成するなんて馬鹿らしいことはしないので、無論自動で行わせるわけですが疲れたので気が向けば書きます。処理は同様にLambdaを使用しますが、トリガーはEventBridgeを使用します。とっても簡単です。

コストについて

「Logが吐かれる度にS3にファイルをPutしてたら、データ転送料は相当かかるんじゃないの?」とか思われるかもしれませんが、僕はAWS Lambda 料金に書かれている
同じ AWS リージョン内における Amazon S3、Amazon Glacier、Amazon DynamoDB、Amazon SES、Amazon SQS、Amazon Kinesis、Amazon ECR、Amazon SNS、Amazon EFS、または Amazon SimpleDB と AWS Lambda 関数の間でのデータ転送は無料です。

という文章を信じています。

参考

S3
Amazon S3 サーバーアクセスのログ記録
Lambda
EventBridge
AWS Lambda 料金
mermaid.js

無料でSSR・ホスティング・API鯖を立てれるVercel。TypeScript・ExpressでAPI鯖を立てる。

$
0
0

Vercel
https://vercel.com

無料で有名なFaaS(Function as a Service)の
Firebase FunctionsNetlify Functionsより使い勝手がいいと思う。

GitHubの捨てアカ作ってログインしようとしたら弾かれた
たぶんアカを作ってからある程度時間が経たないと弾かれるっぽい。

ソースコード

package.json
{"scripts":{"ts-build":"webpack --mode production"},"devDependencies":{"@types/express":"^4.17.8","ts-loader":"^8.0.3","typescript":"^4.0.2","webpack":"^4.44.1","webpack-cli":"^3.3.12","webpack-node-externals":"^2.5.2"},"dependencies":{"express":"^4.17.1","vercel":"^20.1.0"}}
src/index.ts
import*asExpressfrom'express';constapp=Express();// postリクエスト使えるようにするapp.use(Express.json());app.use(Express.urlencoded({extended:true}));app.get('/get/:name',(req:Express.Request,res:Express.Response)=>{try{res.send({name:req.params.name});}catch(error){res.sendStatus(500);}});app.post('/post',(req:Express.Request,res:Express.Response)=>{try{res.send({name:req.body.name});}catch(error){res.sendStatus(500);}});if(!process.env.NOW_REGION){app.listen(process.env.PORT||3000);}exportdefaultapp;
tsconfig.json
{"compilerOptions":{"baseUrl":"./","paths":{"src/*":["src/*"]},"strict":true}}
vercel.json
{"version":2,"builds":[{"src":"index.js","use":"@now/node-server"}],"routes":[{"src":"/.*","dest":"/index.js"}]}
webpack.config.js
constpath=require('path');constnodeExternals=require('webpack-node-externals');module.exports={// モード値を production に設定すると最適化された状態で、// development に設定するとソースマップ有効でJSファイルが出力されるmode:'development',entry:'./src/index.ts',// ファイルの出力設定output:{path:`${__dirname}/`,filename:'index.js',libraryTarget:'this',},module:{rules:[{test:/\.ts$/,exclude:/node_modules/,loader:'ts-loader',options:{configFile:path.resolve(__dirname,'tsconfig.json'),},},],},// import 文で .ts ファイルを解決するため// これを定義しないと import 文で拡張子を書く必要が生まれる。resolve:{// 拡張子を配列で指定extensions:['.ts','.js'],alias:{src:path.resolve(__dirname,'src'),},},externals:[nodeExternals()],};
.gitignore
.vercel
node_modules
index.js

コマンド

TypeScriptをビルド

$ npm run ts-build

ローカルで実行 ポート番号3000で鯖が立ち上がる

$ npx vercel dev

デプロイ

$ npx vercel --prod

ワイの成果物

https://qiita.com/yuzuru2/items/b5a34ad07d38ab1e7378

①コード共有サイト(SPA) React
https://code.itsumen.com

②画像共有サイト(SPA) React
https://gazou.itsumen.com

③チャット(SSR) Nuxt.js
https://nuxtchat.itsumen.com

④チャット(SPA) React
https://chat4.itsumen.com

⑤掲示板(SSR) Next.js
https://board.itsumen.com

⑥掲示板(SPA) Vue
https://board.itsumen.com

⑦レジの店員を呼ぶスマホアプリ(Android)
https://play.google.com/store/apps/details?id=com.itsumen.regi&hl=ja

⑧ブログ(静的サイトジェネレータ) Hugo
https://yuzuru.itsumen.com

簡単レシート印刷 receiptline で行間隔を調整してみた

$
0
0

前回は 80 ミリカメラと 80 ミリフィルムで動画を撮影していました。
「役に立たない機械」感を醸し出していたかもしれないですね。

01.gif

連続で印刷するため、変換ライブラリを少し変更して、自動用紙カットを解除しました。
今回もこの変換ライブラリ lib/receiptline.jsに手を入れてみようと思います。

用紙節約か、読みやすさか

receiptline に添付されているサンプルデータを印刷してみました。
左は TM-T88V で、右は mC-Print3 です。

行間隔が狭いので、文字が詰まって見えます。
今どきのレシートプリンターには用紙節約機能があるので、そういう時代だと思いますが。

02.jpg

行間隔を空ける

行間隔を空けて読みやすくしてみようと思います。
レシートプリンターのコマンドを調べて、一部のコマンドを置き換えてみます。

ESC/POS コマンドリファレンス
https://reference.epson-biz.com/pos/reference_ja/

  • 変更前
    • 行間隔なしコマンド ESC 3 n
  • 変更後
    • 行間隔ありコマンド ESC 2

StarPRNTモード コマンド仕様書
http://sp-support.star-m.jp/SDKDocumentation.aspx

  • 変更前
    • 行間隔なしコマンド ESC 0
  • 変更後
    • 行間隔ありコマンド ESC z n
lib/receiptline.js
//// ESC/POS//const_escpos={// start printing: ESC @ GS a n ESC M n FS ( A pL pH fn m ESC SP n FS S n1 n2 ESC 3 n ESC { n// open: printer => '\x1b@\x1da\x00\x1bM0\x1c(A' + $(2, 0, 48, 0) + '\x1b \x00\x1cS\x00\x00\x1b3\x00\x1b{' + $(printer.upsideDown),open:printer=>'\x1b@\x1da\x00\x1bM0\x1c(A'+$(2,0,48,0)+'\x1b \x00\x1cS\x00\x00\x1b2\x1b{'+$(printer.upsideDown),...};//// StarPRNT MBCS//const_starmbcs={// start printing: ESC @ ESC RS a n ESC RS F n ESC SP n ESC s n1 n2 ESC 0 (SI) (DC2)// open: printer => '\x1b@\x1b\x1ea0\x1b\x1eF\x00\x1b 0\x1bs00\x1b0' + (printer.upsideDown ? '\x0f' : '\x12'),open:printer=>'\x1b@\x1b\x1ea0\x1b\x1eF\x00\x1b 0\x1bs00\x1bz1'+(printer.upsideDown?'\x0f':'\x12'),...};

Node を再起動して再印刷。
行間隔は広がりましたが、縦罫線が途切れてしまっています。

03.jpg

縦罫線を接続する

変換ライブラリをさらに変更。
縦罫線を引く領域では、行間隔を空けないようにします。

ESC/POS

  • 縦罫線の始まり
    • 行間隔なしコマンドを追加 ESC 3 n
  • 縦罫線の終わり
    • 行間隔ありコマンドを追加 ESC 2

StarPRNT

  • 縦罫線の始まり
    • 行間隔なしコマンドを追加 ESC 0
  • 縦罫線の終わり
    • 行間隔ありコマンドを追加 ESC z n
lib/receiptline.js
//// ESC/POS//const_escpos={...// start rules: FS C n ESC t n ... LF// vrstart: (widths, left, right) => '\x1cC0\x1bt\x01' + widths.reduce((a, w) => a + '\x95'.repeat(w) + '\x91', left ? '\x9c' : '\x98').slice(0, -1) + (right ? '\x9d' : '\x99'),vrstart:(widths,left,right)=>'\x1b3\x00\x1cC0\x1bt\x01'+widths.reduce((a,w)=>a+'\x95'.repeat(w)+'\x91',left?'\x9c':'\x98').slice(0,-1)+(right?'\x9d':'\x99'),// stop rules: FS C n ESC t n ... LF// vrstop: (widths, left, right) => '\x1cC0\x1bt\x01' + widths.reduce((a, w) => a + '\x95'.repeat(w) + '\x90', left ? '\x9e' : '\x9a').slice(0, -1) + (right ? '\x9f' : '\x9b'),vrstop:(widths,left,right)=>'\x1b2\x1cC0\x1bt\x01'+widths.reduce((a,w)=>a+'\x95'.repeat(w)+'\x90',left?'\x9e':'\x9a').slice(0,-1)+(right?'\x9f':'\x9b'),...};//// StarPRNT MBCS//const_starmbcs={...// start rules: ESC $ n ... LF// vrstart: (widths, left, right) => '\x1b$0' + widths.reduce((a, w) => a + '\x95'.repeat(w) + '\x91', left ? '\x9c' : '\x98').slice(0, -1) + (right ? '\x9d' : '\x99'),vrstart:(widths,left,right)=>'\x1b0\x1b$0'+widths.reduce((a,w)=>a+'\x95'.repeat(w)+'\x91',left?'\x9c':'\x98').slice(0,-1)+(right?'\x9d':'\x99'),// stop rules: ESC $ n ... LF// vrstop: (widths, left, right) => '\x1b$0' + widths.reduce((a, w) => a + '\x95'.repeat(w) + '\x90', left ? '\x9e' : '\x9a').slice(0, -1) + (right ? '\x9f' : '\x9b'),vrstop:(widths,left,right)=>'\x1bz1\x1b$0'+widths.reduce((a,w)=>a+'\x95'.repeat(w)+'\x90',left?'\x9e':'\x9a').slice(0,-1)+(right?'\x9f':'\x9b'),...};

もう一度 Node を再起動して再印刷。
縦罫線がくっつきました!

04.jpg

動作条件

実は、これが動作するのは printers.jsonの upsideDown が falseの場合です。
この値が trueで、印刷の向きを上下反転している場合はうまく動きません。

printers.json
{"tm_t88v":{"host":"192.168.1.2","port":9100,"cpl":42,"encoding":"cp932","gamma":1.8,"upsideDown":false,"command":"escpos"},"mc_print3":{"host":"192.168.1.3","port":9100,"cpl":48,"encoding":"cp932","gamma":1.8,"upsideDown":false,"command":"starmbcs"}}

奥が深いです。

また何か作ったら投稿します。ではまた!

YOLP の WebAPI で1時間以内の降水予想と雨雲レーダー地図画像を取得する

$
0
0

概要

  • YOLP (Yahoo! Open Local Platform) の Yahoo!ジオコーダAPI + 気象情報API + Yahoo!スタティックマップAPI を使用して1時間以内の降水予想と雨雲レーダー地図画像を取得する
  • 動作確認環境: Node.js v14.9.0 + node-fetch 2.6.1

node-fetch のインストール

$ npm i node-fetch

サンプルコード

'use strict'constfs=require('fs')constqs=require('querystring')constfetch=require('node-fetch')// window.fetch 互換 Fetch API// アプリケーションIDconstAPPID='YOUR_APPLICATION_ID'// テキストにマッチした住所情報を取得するasyncfunctiongetAddressLocation(text){// URLを組み立てるconstparams=qs.stringify({appid:APPID,// アプリケーションIDquery:text,// 検索文字列al:2,// 市区町村レベルの住所を検索exclude_seireishi:false,// 検索対象から政令指定都市レコードを除外するかresults:1,// 検索結果を1件以内に設定output:'json'// レスポンスをJSONにする})consturl='https://map.yahooapis.jp/geocode/V1/geoCoder?'+params// Yahoo!ジオコーダAPIをコールするconstres=awaitfetch(url)if(!res.ok){throwres}constjson=awaitres.json()console.log(JSON.stringify(json,null,''))// 住所情報を取得するif(json.Feature&&json.Feature.length!=0){// ヒットした1つめの住所を使うvarname=json.Feature[0].Namevarll=json.Feature[0].Geometry.Coordinates.split(',')varbbox=json.Feature[0].Geometry.BoundingBoxreturn{address:name,lat:ll[1],lon:ll[0],bbox:bbox}}else{throw'住所にヒットしなかった'}}// 住所に紐ついた緯度経度とバウンディングボックスの4点の緯度経度をつなげた文字列を返すfunctiongetCoordinatesFromBoundingBox(location){varp=location.bbox.split('')varll0=p[0].split(',')varll1=p[1].split(',')varc=location.lon+','+location.lat+''+ll0[0]+','+ll0[1]+''+ll1[0]+','+ll0[1]+''+ll0[0]+','+ll1[1]+''+ll1[0]+','+ll1[1]returnc}// 降水情報を取得するasyncfunctiongetWeatherInfo(location){// 雨の強さを取得したい緯度経度(10点まで可)を指定// フォーマット: 経度,緯度 経度,緯度 経度,緯度 経度,緯度 ... // 経度・緯度の順番でコンマ区切り// 経度・緯度毎に半角スペース区切りconstcoordinates=getCoordinatesFromBoundingBox(location)// URLを組み立てるvarparams=qs.stringify({coordinates:coordinates,appid:APPID,// アプリケーションIDoutput:'json'// レスポンスをJSONにする})consturl='https://map.yahooapis.jp/weather/V1/place?'+params// 気象情報APIをコールするconstres=awaitfetch(url)if(!res.ok){throwres}constjson=awaitres.json()console.log(JSON.stringify(json,null,''))returnjson}// 降水情報をテキストに変換するfunctiongetWeatherText(weather,location){varame=false// 雨が降るか否か// 1箇所でも雨が降る場所があるかfor(varfeatureofweather.Feature){for(varwoffeature.Property.WeatherList.Weather){if(w.Rainfall>0){// 降水強度が0より大きいかame=truebreak}}}if(ame){returnlocation.address+"では、1時間以内に雨が降りそうです。"}else{returnlocation.address+"では、1時間以内には雨が降らないようです。"}}// 地図画像を取得するasyncfunctiongetMapImage(location){// URLを組み立てるvarparams=qs.stringify({width:800,height:600,lat:location.lat,lon:location.lon,z:12,// ズームレベルoverlay:'type:rainfall',// 現在時刻の雨雲レーダーを表示style:'base:monotone',// モノトーンスタイルappid:APPID,// アプリケーションID})consturl='https://map.yahooapis.jp/map/V1/static?'+params// Yahoo!スタティックマップAPIをコールするconstres=awaitfetch(url)if(!res.ok){throwres}returnBuffer.from(awaitres.arrayBuffer())}(async()=>{try{// コマンドライン引数を取得consttext=process.argv[2]console.log('Input: '+text)// 住所情報を取得constlocation=awaitgetAddressLocation(text)// 降水情報を取得constweather=awaitgetWeatherInfo(location)// 降水情報をテキストに変換constweatherText=getWeatherText(weather,location)console.log('Message: '+weatherText)// 地図画像を取得constmapImage=awaitgetMapImage(location)// 地図画像データをファイルに出力awaitfs.promises.writeFile('map.png',mapImage)}catch(err){console.error(err)}})()

実行結果

$ node app.js 福島市
Input: 福島市
{
  "ResultInfo": {
    "Count": 1,
    "Total": 1,
    "Start": 1,
    "Status": 200,
    "Description": "",
    "Copyright": "",
    "Latency": 0.016
  },
  "Feature": [
    {
      "Id": "07201",
      "Gid": "",
      "Name": "福島県福島市",
      "Geometry": {
        "Type": "point",
        "Coordinates": "140.47469440,37.76089730",
        "BoundingBox": "140.22998100,37.62433900 140.57092800,37.97664700"
      },
      "Category": [],
      "Description": "",
      "Style": [],
      "Property": {
        "Uid": "09564f93998c22a98d1921dbd866d75b73f7bd6a",
        "CassetteId": "b22fee69b0dcaf2c2fe2d6a27906dafc",
        "Yomi": "フクシマケンフクシマシ",
        "Country": {
          "Code": "JP",
          "Name": "日本"
        },
        "Address": "福島県福島市",
        "GovernmentCode": "07201",
        "AddressMatchingLevel": "2",
        "AddressType": "市"
      }
    }
  ]
}
{
  "ResultInfo": {
    "Count": 5,
    "Total": 5,
    "Start": 1,
    "Status": 200,
    "Latency": 0.049625,
    "Description": "",
    "Copyright": "(C) Yahoo Japan Corporation."
  },
  "Feature": [
    {
      "Id": "202009082115_140.47469_37.760897",
      "Name": "地点(140.47469,37.760897)の2020年09月08日 21時15分から60分間の天気情報",
      "Geometry": {
        "Type": "point",
        "Coordinates": "140.47469,37.760897"
      },
      "Property": {
        "WeatherAreaCode": 3610,
        "WeatherList": {
          "Weather": [
            {
              "Type": "observation",
              "Date": "202009082115",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082125",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082135",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082145",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082155",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082205",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082215",
              "Rainfall": 0
            }
          ]
        }
      }
    },
    {
      "Id": "202009082115_140.22998_37.624339",
      "Name": "地点(140.22998,37.624339)の2020年09月08日 21時15分から60分間の天気情報",
      "Geometry": {
        "Type": "point",
        "Coordinates": "140.22998,37.624339"
      },
      "Property": {
        "WeatherAreaCode": 3630,
        "WeatherList": {
          "Weather": [
            {
              "Type": "observation",
              "Date": "202009082115",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082125",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082135",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082145",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082155",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082205",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082215",
              "Rainfall": 0
            }
          ]
        }
      }
    },
    {
      "Id": "202009082115_140.57093_37.624339",
      "Name": "地点(140.57093,37.624339)の2020年09月08日 21時15分から60分間の天気情報",
      "Geometry": {
        "Type": "point",
        "Coordinates": "140.57093,37.624339"
      },
      "Property": {
        "WeatherAreaCode": 3610,
        "WeatherList": {
          "Weather": [
            {
              "Type": "observation",
              "Date": "202009082115",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082125",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082135",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082145",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082155",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082205",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082215",
              "Rainfall": 0
            }
          ]
        }
      }
    },
    {
      "Id": "202009082115_140.22998_37.976647",
      "Name": "地点(140.22998,37.976647)の2020年09月08日 21時15分から60分間の天気情報",
      "Geometry": {
        "Type": "point",
        "Coordinates": "140.22998,37.976647"
      },
      "Property": {
        "WeatherAreaCode": 3520,
        "WeatherList": {
          "Weather": [
            {
              "Type": "observation",
              "Date": "202009082115",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082125",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082135",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082145",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082155",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082205",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082215",
              "Rainfall": 0
            }
          ]
        }
      }
    },
    {
      "Id": "202009082115_140.57093_37.976647",
      "Name": "地点(140.57093,37.976647)の2020年09月08日 21時15分から60分間の天気情報",
      "Geometry": {
        "Type": "point",
        "Coordinates": "140.57093,37.976647"
      },
      "Property": {
        "WeatherAreaCode": 3420,
        "WeatherList": {
          "Weather": [
            {
              "Type": "observation",
              "Date": "202009082115",
              "Rainfall": 1.35
            },
            {
              "Type": "forecast",
              "Date": "202009082125",
              "Rainfall": 0.75
            },
            {
              "Type": "forecast",
              "Date": "202009082135",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082145",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082155",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082205",
              "Rainfall": 0
            },
            {
              "Type": "forecast",
              "Date": "202009082215",
              "Rainfall": 0
            }
          ]
        }
      }
    }
  ]
}
Message: 福島県福島市では、1時間以内に雨が降りそうです。

map.png

参考資料

nodejsでスクレイピング入門[阿部寛]

$
0
0

はじめに

卒研でいろいろあって、nodejsでスクレイピングやりそうになってきましたのでそれのメモです、
入門ということで阿部寛のHPから阿部寛の画像をとっていきたいと思います、

スクレイピングとは?

そもそもスクレイピングってなんだろってなるから調べました。
意味はHPから情報を抽出するということらしいでした、
使い道としては、サイトに載ってる情報が欲しいとか、APIがないけどもHPならあるとかだったときはスクレイピングで情報分捕る感じかと、

阿部寛ってだーれ?

日本の俳優、テルマエ・ロマエとかに出ているお方、上田次郎、日本科学技術大学理工学部教授、何故ベストを尽くさないのか?
よくHPが情報系の実験ネタ(?)に扱われるほどすごい公式サイトをもっているお方

できること

nodejsから阿部寛のサイトにある阿部寛の写真と取得できる

つかったもの

  • nodejs
  • yarn

できたもの

最初はHTMLデータとろう!!

  • axios使って阿部寛のHTMLぶんどります
  • なのでaxiosとasyncをyarn addで入れていって、以下のコードを作成します
// ajax関係
const axios = require("axios");
// 阿部寛のサイト
const url = "http://abehiroshi.la.coocan.jp/";

const main = async () => {
    const data = await axios.get(url);
    console.log(data.data);
}

main();
  • これでできますめ!!!!
  • 結果がこんな感じです!!
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
<meta name="GENERATOR" content="JustSystems Homepage Builder Version 20.0.6.0 for Windows">
<meta http-equiv="Content-Style-Type" content="text/css">
<title>�������̃z�[���y�[�W</title>
</head>
<frameset cols=18,82>
  <frame src="menu.htm" marginheight="0" marginwidth="0" scrolling="auto" name="left">
  <frame src="top.htm" marginheight="0" marginwidth="0" scrolling="auto" name="right">
  <noframes>
  <body></body>
  </noframes>
</frameset>
</html>
  • うおおおやったぜ!!

必要な情報とろう!!

  • 今度はFast HTML Parserを使って必要な情報取り出します、
  • のでyarn addでぶちこみます
  • 卒研では画像はURLでほしいので、文字列データ分捕れそうなのでいきます、許して
  • とりあえず動かせるようにしたものがこれです
// ajax関係
const axios = require("axios");
// 阿部寛のサイト
const url = "http://abehiroshi.la.coocan.jp/";
// パーサ
const HTMLparser = require('fast-html-parser');


const main = async () => {
    const data = await axios.get(url);
    console.log(data.data);
    const root = HTMLparser.parse(data.data);
    console.log(root.firstChild.structure);

}

main();
  • 結果がこれです
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
<meta name="GENERATOR" content="JustSystems Homepage Builder Version 20.0.6.0 for Windows">
<meta http-equiv="Content-Style-Type" content="text/css">
<title>�������̃z�[���y�[�W</title>
</head>
<frameset cols=18,82>
  <frame src="menu.htm" marginheight="0" marginwidth="0" scrolling="auto" name="left">
  <frame src="top.htm" marginheight="0" marginwidth="0" scrolling="auto" name="right">
  <noframes>
  <body></body>
  </noframes>
</frameset>
</html>
html
  head
    meta
    meta
    meta
    title
      #text
  frameset
    frame
      frame
        noframes
          body
  • たぶんできた!!!
  • 阿部寛のサイトはサイトを分割してそれをframesetで表示してる感じですね、
  • menuはたぶん右側のメニューだと思われます、
  • 阿部寛の顔面はtopのほうにあると予想されるので、そっちをみましょ、
  • なのでtopの方をaxiosで表示してみます!!
  • コードは以下です
// ajax関係
const axios = require("axios");
// 阿部寛のサイト
const url = "http://abehiroshi.la.coocan.jp/top.htm";
// パーサ
const HTMLparser = require('fast-html-parser');


const main = async () => {
    const data = await axios.get(url);
    console.log(data.data);
}

main();
  • 結果はこんな感じです!!
<!-- saved from url=(0022)http://internet.e-mail -->
<!-- saved from url=(0022)http://internet.e-mail -->
<html>

<head>
<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
<meta http-equiv="Content-Style-Type" content="text/css">
<meta name="GENERATOR" content="JustSystems Homepage Builder Version 20.0.6.0 for Windows">
<title>���� ���̃z�[���y�[�W</title>


</head>


<body background="image/abehiroshi.jpg">
<br>
<h1 align="center">���� ���̃z�[���y�[�W</h1>
<table align="center">
  <tr>
    <td rowspan="2"><img src="abe-top-20190328-2.jpg" width="350" height="414" border="0"><br>
      <br>
      <table width="256">
        <tr>
          <td width="14">&nbsp;</td>
          <td width="230">���� ���i���� �Ђ낵�j<br></td>
        </tr>
        <tr>
          <td>&nbsp;</td>
          <td>���N���� 1964�N6��22��<br></td>
        </tr>
        <tr>
          <td>&nbsp;</td>
          <td>���t�^ A�^<br></td>
        </tr>
        <tr>
          <td>&nbsp;</td>
          <td><a href="prof/prof.htm">�v���t�B�[��</a></td>
        </tr>
        <tr>
          <td>&nbsp;</td>
          <td>&nbsp;</td>
        </tr>
        <tr>
          <td colspan="2"><br>
            If you have any enquiries regarding my TV drama or film, or would like to make an enquiry concerning future projects, please do not hesitate to contact me through the following email address.<br>
            <br>
            mail:<a href="mailto:shigeta@navy.plala.or.jp">shigeta@navy.plala.or.jp</a></td>
        </tr>
    </table>
      <br>
      ����<strong>:</strong><br>
      �Γc�I�t�B�X<br>
      107-0052<br>
  �����s�`��ԍ�9-5-29 <br>
  �ԍ⃍�C�����}���V����303<br>

TEL : +81-3-5410-8585<br>
    FAX : +81-3-5410-0588 </td>
    <td>&nbsp;</td>
    <td><div align="center">�������@�ŐV���@������</div></td>
  </tr>
  <tr>
    <td></td>
    <td>
    <table width="100%" border="0" cellspacing="0" cellpadding="0">
          <tr>
            <td align="left" valign="bottom"><hr width="100%" size="1"></td>
          </tr>
      </table>
<!-- start�@-->
       <table width="100%" border="0" cellspacing="0" cellpadding="0">
          <tr>
            <td align="left" valign="top"><strong>�E�h���}</strong></td>
          </tr>
      </table>
       <table border="0" cellspacing="5" cellpadding="0">
           <tr>
           <td width="70" align="left" valign="top">&nbsp;</td>
           <td>
        <strong>
           <a href="https://www.tbs.co.jp/dragonzakura/" target="_blank">�u�h���S����2�v</a>(��)<font color="#ff0000"></font><br>
        <font color="#ff0000">2020�N�Ă�\�肵�Ă���܂������A�V�^�R���i�E�C���X�����g��̉e���ɂ��������������肢�����܂����B</font></strong></td>
         </tr>
      </table>
<!-- end�@-->
<!-- start�@-->
       <table width="100%" border="0" cellspacing="0" cellpadding="0">
          <tr>
            <td align="left" valign="bottom"><hr width="100%" size="1"></td>
          </tr>
      </table>

       <table width="100%" border="0" cellspacing="0" cellpadding="0">
          <tr>
            <td align="left" valign="top"><strong>�E�f��</strong></td>
          </tr>
      </table>
       <table border="0" cellspacing="5" cellpadding="0">
           <tr>
           <td width="70" align="left" valign="top">&nbsp;</td>
           <td>
        <strong>�u����Ȃ������҂����ցv <br>
        2021�N���J�\��<br>
        </strong></td>
         </tr>
      </table>
      <table border="0" cellspacing="5" cellpadding="0">
           <tr>
           <td width="70" align="left" valign="top">&nbsp;</td>
           <td>
        <strong><br>
        <a href="http://www.hokusai2020.com/" target="_blank">�uHOKUSAI�v</a> <br>
        2021�N ���J�\��<br>
        <font color="#ff0000">�����J�������ɂȂ�܂����B</font><br>
        </strong></td>
         </tr>
      </table>
       <table border="0" cellspacing="5" cellpadding="0">
           <tr>
           <td width="70" align="left" valign="top"></td>
           <td></td>
         </tr>
      </table>
<!-- end�@-->

       <table width="100%" border="0" cellspacing="0" cellpadding="0">
          <tr>
            <td align="left" valign="bottom"><hr width="100%" size="1"></td>
          </tr>
      </table>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    </td>
  </tr>
</table>
</body>
</html>
  • 見た感じ阿部寛の顔面画像はある感じがしますね!!ビンゴ!!!!
  • そこから画像のURLとりましょ!!
  • とりあえずコードはこんな感じです!!
// ajax関係
const axios = require("axios");
// 阿部寛のサイト
const url = "http://abehiroshi.la.coocan.jp/top.htm";
// パーサ
const HTMLparser = require('fast-html-parser');


const main = async () => {
    const data = await axios.get(url);
    const root = HTMLparser.parse(data.data);
    console.log(root.querySelectorAll('img'));
}

main();
  • 結果はこんな感じです!!
[
  HTMLElement {
    tagName: 'img',
    rawAttrs: 'src="abe-top-20190328-2.jpg" width="350" height="414" border="0"',
    childNodes: [],
    classNames: []
  }
]
  • rawAttrsに顔面画像らしきURLがありますね!!
  • あとはそれをとって気合いでとっていきます!!
  • コードはこんな感じです!!
// ajax関係
const axios = require("axios");
// 阿部寛のサイト
const url = "http://abehiroshi.la.coocan.jp/top.htm";
// パーサ
const HTMLparser = require('fast-html-parser');


const main = async () => {
    const data = await axios.get(url);
    const root = HTMLparser.parse(data.data);
    const raw_data = root.querySelectorAll('img')[0].rawAttrs;
    const img_url = raw_data.split("\"")[1];
    console.log(img_url);
}

main();
  • 結果はこんな感じです!!
abe-top-20190328-2.jpg
  • ぶんどれたやった!!

画像を保存する

  • あとは画像を保存するだけですね、、
  • nodejsで画像保存はjimpが良さそうなのでyarn addでjimpをいれます
  • あとは以下のコードで行きます!!
// jimp関係
const jimp = require("jimp");

const main = () => {
    jimp.read("http://abehiroshi.la.coocan.jp/abe-top-20190328-2.jpg", (err, abehiroshi) => {
        if(err) return;
        abehiroshi.write("abehiroshi.jpg");
    });
}

main();
  • これであなたのPCに阿部寛が住み着きましたね!!

おわり

おまけ

  • このコードで阿部寛がいっぱい!!
// jimp関係
const jimp = require("jimp");
const size = 100000000000000000000000000000000;
const main = () => {
    jimp.read("http://abehiroshi.la.coocan.jp/abe-top-20190328-2.jpg", (err, abehiroshi) => {
        if(err) return;
        for(let i = 0;i < size;i++) {
            abehiroshi.write("abehiroshi_"+i+".jpg");
        }
    });
}

main();

参考

JSエコシステムぶらり探訪(2): Node.jsとCommonJS modules

$
0
0

JSエコシステムの進化を語るにはNode.jsを避けて通ることはできません。Node.jsと、それ自身の持つモジュール機能について歴史的な背景を踏まえつつ説明します。

←前目次

Node.js

Node.jsは非同期I/Oを備えたサーバーサイドJavaScriptのための実行環境として2009年に登場しました。1現在はサーバーサイドJavaScriptだけではなく、JavaScriptのビルド環境として無くてはならないものになっています。

要するにNode.jsは、PerlスクリプトやRubyスクリプトと同じようにJavaScriptのコードを実行するための環境です。

main.js
console.log("Hello, world!");
$node main.js
Hello, world!

CommonJS modules (CJS)

CommonJS modulesはNode.jsで主に使われているモジュール形式です。CommonJSという規格群の一部として2009年に登場し、生まれて間もないNode.jsのモジュールシステムもすぐにCommonJSに適合しました。 2

後述するESM (ES Modules) で置き換えられつつあり、CJSのほうが伝統的な形式といえます。

util.js
varprivate_value=42;// exports.square = ... でもよいmodule.exports.square=function(x){returnx*x;};
main.js
varutil=require('./util');console.log(util.square(2));// => 4console.log(util.private_value);// => undefined

また、 module.exportsに別の値を代入することもできます。この場合は exportsにだけ代入しても有効ではないので注意する必要があります。

square.js
// module.exportsへの代入は必須。exportsにも代入しておくと後々便利module.exports=exports=function(x){returnx*x;};// JavaScriptの関数はそれ自体オブジェクトなので、プロパティーに代入できるexports.version="0.1.0";

上の例では、 requireは関数を返します。

main.js
varsquare=require('./square');console.log(square(2));// => 4console.log(square.version);// => "0.1.0"

Node.jsにおいては最初に呼ばれるJavaScriptファイルもモジュールです。

main.js
varx=42;// global is Node.js equivalent of windows// (There's also globalThis usable in both environments)console.log(global.x);// => undefined

Node.jsのCJSの仕組み

CJSのrequireはNode.js (など、それぞれの処理系) が提供するプリミティブ関数ですが、その動作は比較的シンプルに理解できます。つまり、ファイルを発見して読み取り、関数に包んで evalする処理と考えることができます。3

実際にevalされるときは以下のような関数の本体として扱われます。(つまり、 exports, require, module, __filename, __dirnameという5つのローカル変数があらかじめ存在するものとして扱われます。)

(function(exports,require,module,__filename,__dirname){// ファイルの中身});

moduleexportsはあらかじめ以下のように初期化されたものと考えることができます。

varmodule={},exports={};module.exports=exports;// その他、moduleの様々なプロパティーを設定

requiremoduleオブジェクトをキャッシュしておき、eval終了後に module.exportsの値を戻り値として返すと考えることができます。これにより module.exportsexportsの関係についても説明がつきます。

モジュールの副作用とオブジェクトの同一性

以下のようなモジュールを考えます。

module1.js
console.log("Hello, world!");varcounter=1;exports.fresh=function(){returncounter++;};

このモジュールには以下の特徴があります。

  • モジュールのトップレベル処理に副作用がある。
  • モジュールが状態を持っている。

このような場合、モジュールの同一性を気にする必要が出てきます。全く同じ内容の module2.jsをコピーとして作成し、以下のように main.jsから呼び出してみます。

main.js
varmodule1=require('./module1');// => Hello, world!varmodule2=require('./module2');// => Hello, world!console.log(module1.fresh());// => 1console.log(module1.fresh());// => 2console.log(module2.fresh());// => 1console.log(module2.fresh());// => 2console.log(module1.fresh===module2.fresh);// => false

両方の console.logが実行され、 freshは別々にカウントされ、 freshのオブジェクトとしての同一性も falseになりました。

Node.jsでは、パスが同じものは同一モジュールになります4。先ほどの main.jsを書き換えて ./module1を2回インポートするようにしてみます。

main.js
varmodule1a=require('./module1');// => Hello, world!varmodule1b=require('./module1');// (no output)console.log(module1a.fresh());// => 1console.log(module1a.fresh());// => 2console.log(module1b.fresh());// => 3console.log(module1b.fresh());// => 4console.log(module1a.fresh===module1b.fresh);// => true

console.logは1回しか実行されず、 freshは同じカウンタを使うようになり、2つの fresh関数はオブジェクトとしても同一になりました。

冠頭形モジュール

CJSのインポートは単なる requireという関数であり、どこでも呼び出すことができます。これは一見すると便利で妥当な設計に見えますが、実際はNode.js以外の環境にモジュールシステムを移植するにあたってこの「どこでも呼び出せる」という性質が邪魔になってきます。

そこで、以降で解説するモジュールシステムの理解を助けるために、本稿独自の用語として「(CJSの)冠頭形モジュール」という概念を導入します5

定義. あるCommonJSモジュールが冠頭形である (is a prenex-form module)とは、以下を満たすことである。

  • そのモジュールファイルはヘッダ部と本体に分けられる。 (ヘッダ部に続いて本体が来るものとする)
  • ヘッダ部の各文は以下のいずれかの形式である。
    • var <変数名> = require(<文字列リテラル>);
    • let <変数名> = require(<文字列リテラル>);
    • const <変数名> = require(<文字列リテラル>);
    • require(<文字列リテラル>);
  • 本体では require関数は使われていない。

冠頭形であれば、モジュールファイルの中身を実際にevalしなくても、あらかじめ依存先モジュールを決定することができます。

冠頭形ではないものの例としては以下のようなものがあります6

  • 条件つきインポート
  • requireの引数が動的に決まるようなインポート
  • 当該モジュール読み込み時ではなく、あとで必要になってから行うインポート

まとめ

特に重要なのが以下の点です。

  • Node.jsによって「ブラウザー以外のJavaScript実行環境」が大きな地位を獲得した。
  • Node.jsによって、JavaScriptに優れたモジュールシステムがもたらされた。

このことがJavaScriptに2つの大きな課題をもたらしました:

  • Webブラウザーもモジュールシステムの恩恵を受けられるようにすること。
  • Node.jsとWebブラウザーの間のコードの相互運用性を高めること。

これらの課題がJavaScriptバンドラーの誕生、そして各種の新しいモジュールシステムの提案へとつながっていくと考えられます。が、次回はその前に、Node.jsのパッケージシステムについて扱います。

←前目次


  1. Wikipediaの記述によると、それ依然にもサーバーサイドJavaScriptの技術自体は存在していたようです。 

  2. 根拠を探す余裕がなかったのでこのように書きましたが、実際のところNode.jsの初期のモジュールシステムをベースにしてCommonJSが生まれた可能性が高いと思います。 

  3. 他に、ファイルの読み取りが同期的に行われる点、複数回requireしたときにキャッシュする仕組み、巡回参照の処理などを考える必要がある 

  4. シンボリックリンクについては次回言及予定 

  5. これについて、より広く使われている名称があれば教えていただけるとありがたいです。 

  6. バンドラーによっては、ここに挙げたような例をうまく処理できてしまう場合もありますが、それでも一般的な場合を全てカバーするのは困難です。 

いまさらNode.js入門

$
0
0

記事概要

環境構築まわりは誰か出来る人ひとりがやってしまえば
触る必要がない(むしろ触らないほうが良いくらい)ので
なかなか知識が付きにくい。
という事で少し勉強してみた事をまとめてみました!


Node.js とは


そもそもNode.jsとは何ぞやというところですが、
めちゃくちゃ簡単に言うと、サーバーサイドで動くJavaScript環境の事です。

と言葉で聞いてもピンと来ないので、実際にどういう事か
次のスライドで見てみましょう。


やっている事の説明は次のスライドで行っています。
qiita-test.gif


前のスライドでやっている事

  1. testを出力するスクリプトを作成
  2. スクリプトファイルをダブルクリックで実行しようとするとエラーが出る
  3. Node.jsを使用してコマンドラインから作成したスクリプトを実行するとtestが出力される

正確には違うかもしれませんが、
これがサーバーサイドで動くってことかぁ
となんとな~く感じでいただけたかと思います。

では次に、Node.jsでよく使用される
npmについて見ていきましょう。


npm とは


npmはパッケージ管理システムの一種です。

npmには凄い人たちが作った、
凄いモジュールが沢山あります。
Node.jsではそれらのモジュールをインストールする事で
使用することができるのですが、そのインストールの際
npmを使用します。


モジュールは依存関係を持っている事が多く、
モジュールAを使用するためにはモジュールBが必要で
そのモジュールBを使用するためにはモジュールCが~…
みたいなことが起きます:cry:


npmはこのような事を解決して
モジュールAをインストールする。と実行するだけで
そのモジュールを実行するために必要なモジュールを
併せてインストールしてきてくれます:relaxed:


モジュールの使用方法


実際にモジュールを使用してみましょう。
まずは、今回作業するディレクトリを作成してください。
今回、私はデスクトップにtestという
ディレクトリを作成して説明を行っていきます。

※ Node.jsはインストール済みを想定しています。
※ ターミナルはgit bashを使用しています。


まずは該当のディレクトリに移動して、
下記コマンドを打ってみましょう。

npm init

色々と入力を求められますが、すべてEnterで大丈夫です。
するとpackage.jsonというファイルができるので
このファイルを開いてみましょう。


package.jsonの中の"scripts"
"test"というscriptがあるかと思います。
この部分を下記の様に書き換えてみましょう。

"scripts":{"test":"echo hello world !"}

そして、下記コマンドを実行すると「hello world !」が
表示されるはずです!

npm run test

これが基本的なnpm-scriptの実行方法です。


npm-scriptを実行する際はpackage.jsonがある
ディレクトリでnpm run [スクリプト名]
実行が可能です!

次はnpmでモジュールをインストールして
そのモジュールを使用したスクリプトを
作成していきましょう。


今回は例としてonchangeモジュールを
使用してみましょう。このモジュールはファイルの変更を
検知してくれるという機能になります。
下記コマンドでインストールを行いましょう。

npm install onchange

インストールが完了したのを確認したら
package.jsonを見てみましょう。すると、
"dependencies"という項目に"onchange"
追加されていると思います。これがあることで、
npm installというコマンドを打つだけで、
onchangeモジュールをインストールしてきてくれます。


つまり、複数人で開発を行う際package.json
共有されていれば、その階層でnpm installを行うだけで
"dependencies"にあるモジュール群を全てインストール
してきてくれるという仕組みです!

必要なモジュールをひとりひとりが一個ずつ
インストールする必要が無いのは、このおかげです。


それではインストールしてきたonchangeモジュールを
実際に使ってみましょう。詳細な使用方法は
公式にあるので、そちらを参考にしてください。

まずは今回使用しているディレクトリの中に、srcというディレクトリを作成してください。


次に、package.jsonを開いて先ほど編集した
"scripts""test"の箇所を下記の様に
変更してください。

"scripts":{"test":"onchange src/* -- echo {{changed}}"}

先ほどのスクリプトでやっている事としては
下記のような感じです。

onchange[監視するファイル]--[変更があったときに実行されるスクリプト]

今回は、srcディレクトリ内で、変更があったら
変更があったファイル名を出力する。
という単純なものです。

途中で使われている{{changed}}というのも、
onchangeモジュールの機能の一つで、
変更が検知されたファイルの
ファイル名を教えてくれます。


では、実際にスクリプトを実行してみましょう。

npm run test

すると、監視モードになると思うので、
作成したsrcディレクトリに適当なファイルを作成したり
編集したりしてみてください。
変更したファイル名が出力されたら成功です:sunglasses:


以上がNode.jsの簡単な説明になります!
今回使用したonchangeモジュールは使い方次第で
とても便利に使用できるので、とてもおすすめです。

今までNodeを使ってるけど、どんなものなのか
あまり理解してなかったので、同じような人の
助けになれば幸いです:blush:


お わ り


デプロイコマンドを実行する際に対話形式で確認

$
0
0

デプロイのコマンドを実行する際に、環境ごとで確認をするためのスクリプトを書きます。
指定された入力があれば、そのあとの処理を続けて実行、指定と違えばそこで終了します。

package.json
{"scripts":{"deploy":"sh confirmation.sh STG && node deploy"}}
confirmation.sh
#!/bin/bashENV_NAME=$1function ConfirmExecution(){echo"${ENV_NAME}環境のスクリプトを実行しますか?"echo"実行する場合は${ENV_NAME}と入力してください"read input

  if[-z$input];then
    ConfirmExecution
  elif[$input=${ENV_NAME}];then
    echo"スクリプトを実行します。"else
    echo"スクリプトを終了します。"exit 1
  fi}

ConfirmExecution
deploy.js
console.log('deploy!!')

実行

$ npm run deploy

MATERIAL-UIのMultiple SelectでChipから選択を解除する

$
0
0

はじめに

React + Typescript + MaterialUIでフロントエンドを作成していたときに実現したいことができなかったので参考程度に投稿します。

実現できなかった原因

Chipの×ボタンを押してもSelectのメニューが出てきてしまう

実現したいこと

MaterialUIの公式ドキュメントに書かれていた「Multiple Select(Chip)」でChipの×ボタンからも選択を解除したい!
last.gif

結論

Chipコンポーネントに以下プロパティを追加

onMouseDown={(event)=>{event.stopPropagation();}}

前提条件

  • typescript,React,MaterialUiについてある程度の知識がある。
  • create-react-appでアプリケーションが作成済み
npx create-react-app {your_app_name} --template typescript
npm install @material-ui/core

Multiple SelectのChipを動かしてみる

まずは何も考えずコードをすべてコピーしてApp.tsxに貼り付ける。
次にChip以外のところは邪魔なのでChipに関連するものだけを残す。
そうするとコードは以下のようになります。

App.tsx
importReactfrom'react';import{createStyles,makeStyles,useTheme,Theme}from'@material-ui/core/styles';importInputfrom'@material-ui/core/Input';importInputLabelfrom'@material-ui/core/InputLabel';importMenuItemfrom'@material-ui/core/MenuItem';importFormControlfrom'@material-ui/core/FormControl';importSelectfrom'@material-ui/core/Select';importChipfrom'@material-ui/core/Chip';constuseStyles=makeStyles((theme:Theme)=>createStyles({formControl:{margin:theme.spacing(1),minWidth:120,maxWidth:300,},chips:{display:'flex',flexWrap:'wrap',},chip:{margin:2,},noLabel:{marginTop:theme.spacing(3),},}),);constITEM_HEIGHT=48;constITEM_PADDING_TOP=8;constMenuProps={PaperProps:{style:{maxHeight:ITEM_HEIGHT*4.5+ITEM_PADDING_TOP,width:250,},},};constnames=['Oliver Hansen','Van Henry','April Tucker','Ralph Hubbard','Omar Alexander','Carlos Abbott','Miriam Wagner','Bradley Wilkerson','Virginia Andrews','Kelly Snyder',];functiongetStyles(name:string,personName:string[],theme:Theme){return{fontWeight:personName.indexOf(name)===-1?theme.typography.fontWeightRegular:theme.typography.fontWeightMedium,};}exportdefaultfunctionMultipleSelect(){constclasses=useStyles();consttheme=useTheme();const[personName,setPersonName]=React.useState<string[]>([]);consthandleChange=(event:React.ChangeEvent<{value:unknown}>)=>{setPersonName(event.target.valueasstring[]);};return(<div><FormControlclassName={classes.formControl}><InputLabelid="demo-mutiple-chip-label">Chip</InputLabel><SelectlabelId="demo-mutiple-chip-label"id="demo-mutiple-chip"multiplevalue={personName}onChange={handleChange}input={<Inputid="select-multiple-chip"/>}renderValue={(selected)=>(<divclassName={classes.chips}>{(selectedasstring[]).map((value)=>(<Chipkey={value}label={value}className={classes.chip}/>))}</div>)}MenuProps={MenuProps}>{names.map((name)=>(<MenuItemkey={name}value={name}style={getStyles(name,personName,theme)}>{name}</MenuItem>))}</Select></FormControl></div>);}

そしてこれをnpm startしてhttp://localhost:3000でみると
first.gif
よさそう!※拡大してます

Chipの×ボタンを表示する

Chipに×ボタンを表示するにはChipコンポーネントにonDeleteプロパティを追加すればいい。

Apptsx
<Chipkey={value}label={value}className={classes.chip}onDelete={()=>{alert(value)}}/> #ここを追

今回は×ボタンをがクリックされたことを確認するために、alertでクリックしたChipのラベルを表示するようにした。
そして再度npm start
second.gif

あれ...?何度押しても×ボタンがクリックできずにSelectのメニューが出てきてしまう。

対処法

Chipコンポーネントに以下プロパティを追加

App.tsx
<Chipkey={value}label={value}className={classes.chip}onDelete={()=>{alert(value)}}onMouseDown={(event)=>{event.stopPropagation()}}#ここを追加/>

そして再度npm start
third.gif

今度はきちんとクリックされてアラートが出ていますね。

Chip削除の処理を追加する

先ほど入れたalertの処理をChip削除(personName内の名前を削除)する処理に変える。

新しくchipDeleteという関数を用意する

 const chipDelete = (name: string) => {
    setPersonName(personName.filter(value => value !== name))
  }

そうするとApp.tsxfunction MultipleSelect()は以下のようになる

App.tsx
exportdefaultfunctionMultipleSelect(){constclasses=useStyles();consttheme=useTheme();const[personName,setPersonName]=React.useState<string[]>([]);consthandleChange=(event:React.ChangeEvent<{value:unknown}>)=>{setPersonName(event.target.valueasstring[]);};constchipDelete=(name:string)=>{setPersonName(personName.filter(value=>value!==name))};return(<div><FormControlclassName={classes.formControl}><InputLabelid="demo-mutiple-chip-label">Chip</InputLabel><SelectlabelId="demo-mutiple-chip-label"id="demo-mutiple-chip"multiplevalue={personName}onChange={handleChange}input={<Inputid="select-multiple-chip"/>}renderValue={(selected)=>(<divclassName={classes.chips}>{(selectedasstring[]).map((value)=>(<Chipkey={value}label={value}className={classes.chip}onDelete={(event)=>chipDelete(value)}onMouseDown={(event)=>{event.stopPropagation();}}/>))}</div>)}MenuProps={MenuProps}>{names.map((name)=>(<MenuItemkey={name}value={name}style={getStyles(name,personName,theme)}>{name}</MenuItem>))}</Select></FormControl></div>);}

そしてnpm start
last.gif

うまくいった!!!!:v:

終わりに

今思うとこんなにすっとできてしまうなんて。。。
ただ単にコピペするだけではなくてどのような処理が行われていて、どんなオプションのプロパティがあるのかを確認しないといけないですね。

社内ネットワークからIBM Watsonにつながらない

$
0
0

はじめに

社内ProxyのおかげでIBM WatsonのAPIが通らかったので対処法を記載します。
(今回はWatson Discoveryです)

結論

node-sdkのところに書いてありました。:open_hands:
Use behind a corporate proxy

npm install tunnel

前提条件

  • Node v10.16.3
  • npm v6.9.0
  • IBM CloudのWatsonDiscoveryのサービスが既にあり、collectionが作成済みであること

Discoveryについてはこちらの記事が参考になると思います。
Discoveryってなんぞ?という方はまずこちらの記事を見ることをおすすめします。
【2019/2月 全面更新!】Watson Discovery Serviceが日本語対応したので、触ってみた【SDUやってみた】編

社内ProxyのせいでAPIが通らないのなら、Nodeやnpmもインストールできないのでは・・・?
というご意見はごもっともですが、Node,npmはすでに実行できるという前提でお願いします。今回はあくまでIBM Watsonに焦点を当てます。

まずはWatsonのAPIを実行してみる

好きなディレクトリにて

npm init

何か聞かれますがすべてEnterでOK

Watson APIs Node.js SDK のインストール

Discoveryのインスタンスをクリックすると次のような画面が出ると思います。
その画面のAPIリファレンス→Nodeタブへ移ると記載があります
top.png
apire.png

npm install ibm-watson@^5.7.0

APIの実行

まずは簡単に今あるCollectionのリストを表示してみましょう。

APIリファレンスでは左側のMethods>Collection>List collectionのところに記載があります。
丁寧に右側にExample requestがあり、クリックでコピーができるようになっているのでコピーしましょう。
実行するファイルは適当にcollection.jsにします。

collection.js
constDiscoveryV1=require('ibm-watson/discovery/v1');const{IamAuthenticator}=require('ibm-watson/auth');constdiscovery=newDiscoveryV1({version:'2019-04-30',authenticator:newIamAuthenticator({apikey:'{apikey}',//自身の情報に書き換え}),serviceUrl:'{url}',//自身の情報に書き換え});constlistCollectionsParams={environmentId:'{environment_id}',//自身の情報に書き換え};discovery.listCollections(listCollectionsParams).then(listCollectionsResponse=>{console.log(JSON.stringify(listCollectionsResponse,null,2));}).catch(err=>{console.log('error:',err);});

しかし、このままでは実行してもエラーが出てしまいます。
どこのDiscoveryのどのインスタンスに対してAPIを呼ぶかが記載されていないからです。
上記ソースコードの「//自身の情報に書き換え」をそれぞれ書き換えます(中括弧もいらない)

apikey,url
api,url.png

environment_id
environ.png

書き換えた後に実行

node collection.js

そうすると無事エラーが返ってきます。:v:

error: { Error: connect ETIMEDOUT 23.41.93.168:443
    at RequestWrapper.formatError (xxxxxxxxxx\node_modules\ibm-cloud-sdk-core\lib\request-wrapper.js:224:21)
    at C:xxxxxxxxxxxxxxx\node_modules\ibm-cloud-sdk-core\lib\request-wrapper.js:212:25
    at process._tickCallback (internal/process/next_tick.js:68:7)
  message: 'connect ETIMEDOUT 23.41.93.168:443',
  statusText: 'ETIMEDOUT',
  body:
   'Response not received - no connection was made to the service.' }

これでAPIを実行する環境ができました。

対処方法

node-sdkのところに書いてありました。:open_hands:
Use behind a corporate proxy
パッケージの「tunnel」を使ってね、とのこと
早速インストール

npm install tunnel

そしてgithubのExampleをみつつ

collection.js
constDiscoveryV1=require("ibm-watson/discovery/v1");const{IamAuthenticator}=require("ibm-watson/auth");consttunnel=require("tunnel");// https AgentconsthttpsAgent=tunnel.httpsOverHttp({proxy:{host:"xxxxxx.com",//httpとかいらないport:8080,},});constdiscovery=newDiscoveryV1({version:"2019-04-30",authenticator:newIamAuthenticator({apikey:"xxxxxxxxxxxxxxxxxxxxxxxxxxxx",httpsAgent,proxy:false,}),httpsAgent,proxy:false,serviceUrl:"https://xxxxxxxxxxxxxx",});constlistCollectionsParams={environmentId:"xxxxxxxxxxxxxxxxxxxxxxxxxxxx",};discovery.listCollections(listCollectionsParams).then((listCollectionsResponse)=>{console.log(JSON.stringify(listCollectionsResponse,null,2));}).catch((err)=>{console.log("error:",err);});

そして実行すると・・・
※idなどの情報はマスクしました

{
  "status": 200,
  "statusText": "OK",
  "headers": {
    "content-type": "application/json;charset=utf-8",
    "access-control-allow-origin": "*",
    "access-control-allow-methods": "GET, PUT, POST, DELETE, OPTIONS",
    "cache-control": "no-cache, no-store, no-transform, max-age=0",
    "pragma": "no-cache",
    "expires": "0",
    "x-content-type-options": "nosniff",
    "x-xss-protection": "1",
    "content-security-policy": "default-src 'none'",
    "access-control-allow-headers": "Origin, Content-Type, Content-Length, Accept, X-Watson-Authorization-Token, X-WDC-PL-OPT-OUT, X-Watson-UserInfo, X-Watson-Learning-Opt-Out, X-Watson-Metadata",
    "strict-transport-security": "max-age=31536000; includeSubDomains;",
    "x-global-transaction-id": "4085110c3d0bc216af495b254da2ab8b",
    "x-dp-watson-tran-id": "4085110c3d0bc216af495b254da2ab8b",
    "content-length": "340",
    "x-edgeconnect-midmile-rtt": "165",
    "x-edgeconnect-origin-mex-latency": "222",
    "date": "Wed, 09 Sep 2020 13:27:25 GMT",
    "connection": "close"
  },
  "result": {
    "collections": [
      {
        "collection_id": "xxxxxxxxxxxxxxxxxxxx",
        "name": "test",
        "configuration_id": "xxxxxxxxxxxxxxxxxxxxx",
        "language": "ja",
        "status": "active",
        "description": null,
        "created": "2020-09-09T12:55:36.283Z",
        "updated": "2020-09-09T12:55:36.283Z"
      }
    ]
  }
}

めでたしめでたし:clap::clap:

終わりに

私が困っているようなことなんて、とっくにほかのだれかが困っていて解決策を出してくれているのできちんと公式のドキュメントをしっかり見よう!!
英語翻訳バンザイ:point_up:

DiscoveryのQueries>Query a collectionではまったとこ

クエリーの返り値フィールドを指定できるパラメーターの書き方がややこしくてはまった。
複数なのでリストかと思っていたら全然違かった。
正しくは下記

constqueryParams={environmentId:'{environment_id}',collectionId:'{collection_id}',_return:'id,metadata.name' //ここ};

無料でSSR・ホスティング・API鯖を立てれるVercel。GitHub Actionsで自動デプロイ。

$
0
0

あらかじめローカルでプロジェクトを作っておく

環境変数

VECEL_TOKEN → コンパネのSettings→Tokens
VECEL_ORG_ID → .vercel/project.jsonに書いてる
VECEL_PROJECT_ID → .vercel/project.jsonに書いてる

ソースコード

デプロイするファイル

public/index.html
<!DOCTYPE html><htmllang="ja"><head><metacharset="UTF-8"/><metaname="viewport"content="width=device-width, initial-scale=1.0"/><title>Document</title></head><body><h1>hello</h1></body></html>
.github/workflows/main.yml
# 何でもいいから名前をつける。今回はCIname:CI# リポジトリにpushされた時に動くon:[push]# 必ず書くjobs:# jobの名前を書くaaa:# osはubuntuを使うruns-on:ubuntu-latest# 実行手順steps:# masterブランチにチェックアウト-uses:actions/checkout@master# 何でもいいから名前をつける-name:a# 実行するコマンドを書くrun:|mkdir .vercelecho {\"orgId\": \"${{ secrets.VECEL_ORG_ID }}\", \"projectId\": \"${{ secrets.VECEL_PROJECT_ID }}\"} > .vercel/project.jsonnpx vercel --token ${{secrets.VECEL_TOKEN}} --prod

ワイの成果物

https://qiita.com/yuzuru2/items/b5a34ad07d38ab1e7378

①コード共有サイト(SPA) React
https://code.itsumen.com

②画像共有サイト(SPA) React
https://gazou.itsumen.com

③チャット(SSR) Nuxt.js
https://nuxtchat.itsumen.com

④チャット(SPA) React
https://chat4.itsumen.com

⑤掲示板(SSR) Next.js
https://board.itsumen.com

⑥掲示板(SPA) Vue
https://board.itsumen.com

⑦レジの店員を呼ぶスマホアプリ(Android)
https://play.google.com/store/apps/details?id=com.itsumen.regi&hl=ja

⑧ブログ(静的サイトジェネレータ) Hugo
https://yuzuru.itsumen.com

ワイのLINE: https://line.me/ti/p/-GXpQkyXAm

お仕事待ってます^^

え!? わずか3分でローカルにTypeScriptの実行環境を!?

$
0
0

できらぁ!(様式美)

ということでローカルにTypeScriptの実行環境を作ります。すぐできます。

TypeScriptを使うだけなら、TypeScript playground等を使えばいいと思うのですが、「○○のパッケージを試したい。ついでだからTypeScriptも使いたい」という欲張りさんはローカルに環境構築したくなることもあるでしょう。え? codesandbox? 知らんなぁ。

とにかくローカルにTypeScriptの実行環境を作っていきます。ゴールは「コンパイルして出来たjsファイルをnodeコマンドで実行するところ」までです。

事前準備

以下は事前に準備できてるとします。出来てない方は適当にググってください。

  • node, npm(yarn)

ローカルにTypeScriptの実行環境を作成

ここから本題です。
あと私はyarn派なのでyarnを使います。

プロジェクトの作成

プロジェクトディレクトリを作成して、package.jsonを作りましょう。

$ mkdir typescript_try
$ cd typescript_try/
$ yarn init

yarn init後に色々聞かれますが、とりあえず全部Enterで良いです。(ちゃんと設定したい人はしてください)

TypeScriptのインストール

$ yarn add -D typescript @types/node

typescript等はもちろん開発でしか使わないので、-Dはつけましょう(すぐ忘れる)

tsconfig.json

これが無いとコンパイル出来ないので作ります。ルートディレクトリに置いて下さい。

$ touch tsconfig.json
tsconfig.json
{"compilerOptions":{"lib":["ESNext"],"module":"CommonJS","outDir":"dist",// コンパイル後に生成されるJSファイルの置き場所をTSCに指示"sourceMap":true,"strict":true,"target":"ES2015"},"include":["src"]// TSCがTypeScriptファイルを見つけるためにどのディレクトリを探せば良いか?の指定}

なおこの内容は、オライリーのTypeScript本の2.3.1 tsconfig.json に記載された内容をベースに作成しました。(yarn tsc --initでも内容は異なりますが最低限のものを作れます。こっちの方が普通かも?)

詳細は割愛しますが、以降の説明に関連する2つのパラメータに関してはjsonのコメントに説明を記載しました。なおTSCはTypeScriptのコンパイラのことです。

他の項目に関してはググってください。もしくはオライリー本を買って下さい! 超良書です。

またこの時点では、"src"/"dist" フォルダが無いため、エディタによってはエラーが表示されるかもしれませんが、そこは一旦スルーして下さい。

TSファイルの作成

実行したいTypeScriptファイルを作成します。
tsconfig.jsonのincludeで指定した通り、srcディレクトリを作成して、その下に作って下さい。

$ mkdir src
$ touch src/index.ts
src/index.ts
consthello:string='Hello TypeScript!'console.log(hello)

コンパイル後に生成されるJSファイルの置き場所を作成

ディレクトリを作るだけでOKです。

$ mkdir dist

もし先ほどtsconfig.jsonでエラーが出ていた場合は、この後エラーが消えていることを確認して下さい。

TSファイルのコンパイル

作成したTypeScriptファイルをコンパイルします。

$ yarn tsc
yarn run v1.22.4
$ node_modules/.bin/tsc
✨  Done in 1.39s.

生成されたJSファイルはdist下に保存されているはずです。確認してみましょう。

dist/index.js
"use strict";consthello='Hello TypeScript!';console.log(hello);//# sourceMappingURL=index.js.map

それっぽく出来てますね!

生成されたJSファイルの実行

nodeコマンドで生成されたJSファイルを実行します。

$ node dist/index.js
Hello TypeScript!

これでローカル環境でTypeScriptのコードを実行できました。簡単でしたね!

さいごに

「手元でJavaScript周りのパッケージの動作確認等をする時、サラッとTypeScript使えてたらカッコよくない?」という不純な気持ちで書きました。

ただパッケージによっては、別途TypeScript用の設定が必要だったりするのでそこはご注意くださいm(_ _)m

Viewing all 8902 articles
Browse latest View live