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

これからはじめる、Gatsbyのインストールから静的サイトのビルドまで

Gatsbyは次のWordpressとも言われている、Reactベースのオープンソースフレームワーク。

超高速なWebサイトやブログ、アプリを簡単に作ることができ、今最も注目されているCMSツールでもあります。

ここではGatsbyをこれからはじめる人のために、インストール〜静的サイトのビルドまでをサクッと解説していきます。

Node環境のインストール

まずは環境のチェック。nodeは11.10以降にする必要がある。

brew使ってたので、brewでnodeをアップデートする。

node入ってない人はここからダウンロードできる。

brew upgrade node

// nodeをインストールしてない場合
brew install node

インストールできたらnodeのバージョンチェック。

node -v
v13.11.0

Gatsbyのインストール

npm install -g gatsby-cli
gatsby new gatsby-site
cd gatsby-site
gatsby develop

http://localhost:8000にアクセス。ページが表示されればOK。

Home___Gatsby_Default_Starter.png

静的サイトのビルド(生成)

コマンドで自動的にファイルを作成してくれる。

gatsby build

ビルドすると、puglicフォルダに静的サイトが作成される。あとはサーバーにアップすればWebサイトを表示できる。

Gatsbyのスターターライブラリを使ったインストール

Gatsbyはデフォルトだけでなく、ブログやWebサイト、ポートフォリオサイトなどのスターターライブラリがあり、効率よく開発をスタートできる。

スターターライブラリ
https://www.gatsbyjs.org/starters/?v=2

ブログを作りたい場合はこちら。

gatsby new gatsby-starter-blog https://github.com/gatsbyjs/gatsby-starter-blog

Gatsbyのリソース

ドキュメントがしっかりしてるので、そこ見ればだいたいのことはわかる。

プラグインやライブラリなどもリンクされてるので、公式のリソース集はチェックしておきましょう。

最新情報はTwitterやRedditを活用。

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

discord.js最新版での罠(私的メモ)

discord.jsが11.xから12.xにアップデートされたので自分が引っかかりまくった点をサクッと書いていきます

Nodejsとdjsのバージョン

discord.js 12.xからはどうやらnodejs 12.xからじゃないと出来ないっぽくて見事にハマってました
自分はちょっと都合でnodejs8.10くらいを使ってまして、discord.jsを入れてbotを起動させようとしたら謎の場所でエラー吐いてまして…色々調べてたらバージョン関係で使えなかったことがわかって仕方なくnodejsとnpmを最新版にして使いました

djsの仕様変更

色々と変わったので自分が知ってる限りの仕様変更部分をまとめます。

xxxs系

xxxs系は<client>.channels<message>.guild.rolesなどの複数の情報が入ってるやつです。
11.xでは<client>.guilds.get('channelId')などで出来たのですが、最新版になって<client>.guilds.**cache**.get('channelId')と、cacheをつけないとArrayとして認識されなくなれました(xxxsはmanager系になってました、それでcacheにCollection系が移動されたんだと思います。)

ボイス系

ボイス系は<message>.member.voiceです
前のバージョンは<message>.member.voiceChannel.join()とかだったと思うんですけど、最新版だと<message>.member.voice.channel.join()になってます。
あとはモジュールの変更ですかね
旧モジュール:
node-opus, ffmpeg-binaries
新モジュール:
@discordjs/opus, ffmpeg-static
opusscriptはまだ使えるかは不明。

追記(2020/3/31)

メッセージ関連

最新版djsになって、メッセージを送信する時のやつが完全に消えました

12.xで消えたもの
const ch = <message>.channel;
ch.sendCode();
ch.sendEmbed();
ch.sendFile();
ch.sendFiles();
ch.sendMessage();

これは11.xでもDEPRECATEDが付いていたので多分知ってた人は知ってたと思います。

ついでに、基本メッセージ送信は<message>.channel.send();です、リプライは<message>.reply();です。

message系に関して

embedのRichEmbedMessageEmbedに変更されています。

自分的にはこれ使うよりオブジェクト型で書いた方が直感的かなーと思ってたりしてます

embed-sample.js
<message>.channel.send({
  title: 'Hello',
  description: 'Discord.js!'
});

こんな感じの書き方があるのでぜひこの書き方をおすすめしたい

おわり

自分が見てきたとこだとこれくらいですかね、他にもあったような気がするので思い出したら追記しようと思います。

参考

discord.js

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

autodetect’: Could not find a JavaScript runtime. See https://github.com/rails/execjs for a list of available runtimes.

masterからサブブランチに切り替えて、

rails s

をしたら、

autodetect’: Could not find a JavaScript runtime. See https://github.com/rails/execjs for a list of available runtimes.

とエラー分が表示され、Javascriptになにか関係が??初めて見るエラー分で、何かしたかと思っていると、、、、

最近、何かとこのエラーに遭遇する人が多いだとか、、、、、

私の場合は、masterからサブブランチに切り替えたときに起こりましたが、これはタイミングが被っただけではないでしょうか。

解決策としては、gemfileに

gem 'therubyracer'
gem 'libv8'

として、bundle installでいけるのかと、思いきやここでエラー分が出たため、node.jsをインストールすることにしました。

一度、上記のgemをインストールできるか試してみてください。

node.jsをhomebrewを使ってインストールする場合は、

 brew install nodejs

でインストールできます。

見事rails sが可能になりました。

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

AWS IoT のクライアントデバイス環境を簡単に作るスクリプト(Node.js v2 版)

ちょこっとテストするための、Node.js版のAWS IoT Device SDK v2のPubSubをすぐに試すスクリプトです。CLI上で数行でできます。
Python版はこちら だいたい同じです

Cloud9上での設定を想定しています。マネコンで作成するのが手間な場合に使います。
何をやっているか等は説明しません。。
あと、手動で何発かメッセージを送るだけであれば、IoT Coreのテスト機能を使うのがよいです。

準備

Cloud9 の環境にはUbuntuを選びます。Amazon Linuxだと、GLIBC_2.25 が無いというエラーが出ております。
Cloud9の環境を作成し、以下をEnvironmentディレクトリ下に置きます。

setup-node-v2.sh
mkdir $THING_NAME
cd $THING_NAME
POLICY_NAME=${THING_NAME}_Policy

aws iot create-thing --thing-name ${THING_NAME}

git clone https://github.com/aws/aws-iot-device-sdk-js-v2.git
cd aws-iot-device-sdk-js-v2/samples/node/pub_sub/
npm install

wget -O rootca.pem \
    https://www.amazontrust.com/repository/AmazonRootCA1.pem

aws iot create-keys-and-certificate --set-as-active \
    --certificate-pem-outfile    certificate.pem \
    --public-key-outfile         public_key.pem  \
    --private-key-outfile        private_key.pem \
    --query certificateArn

echo -n CERTIFICATE_ARN: 
read str 
CERTIFICATE_ARN=$str

aws iot create-policy                     \
        --policy-name ${POLICY_NAME}      \
        --policy-document file://../../../../../policy.json

aws iot attach-thing-principal             \
        --thing-name $THING_NAME           \
        --principal $CERTIFICATE_ARN

aws iot attach-principal-policy            \
        --policy-name $POLICY_NAME         \
        --principal $CERTIFICATE_ARN

ポリシーは適宜修正すべきですが、一旦なんでも有りで。

policy.json
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action":["iot:*"],
    "Resource": ["*"]
  }]
}

実行

npm install aws-iot-device-sdk-v2

aws iot describe-endpoint --endpoint-type iot:Data-ATS 
export ENDPOINT=yourendpoint-ats.iot.ap-northeast-1.amazonaws.com
export THING_NAME=mything
./setup-node-v2.sh
cd $THING_NAME/aws-iot-device-sdk-js-v2/samples/node/pub_sub
node dist/index.js --endpoint $ENDPOINT --root-ca rootca.pem --cert certificate.pem --key private_key.pem

別のThingで試すときは、THING_NAMEを書き換えて実行します。

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

Node.js(express+ejs)のWebアプリサンプルをDocker上で動かす

はじめに

  • Node.jsをDockerで起動し、適当なWeb画面を表示させるところまでをやります
  • Node.jsでのサンプルはAkinari Tsugoさんの記事を参考に実装しました

やったこと

  • Node.js(express + ejs)の環境をdockerで構築する
  • express + ejsを使用したWebアプリサンプルを作成し、docker上で起動させる

使用するフレームワークについて

expressとは

  • Node.jsでWebアプリを開発する際に使用するフレームワーク
  • 画面遷移とかWebアプリ開発に必要な諸々をサポートする機能あり
  • expressの公式はこちら

ejsとは

  • JavaScriptでHTMLを作成できるテンプレート言語
  • JavaでいうところのJSPに相当する
  • ejsの公式はこちら

node.jsをdockerで構築する

Dockerfileの作成

  • 鉄板のalpineを選択
  • express、ejsのみをnpmで投入
FROM node:alpine

RUN npm install express
RUN npm install ejs

# 作業ディレクトリへ移動
WORKDIR /app

# 3000番ポートを公開
EXPOSE 3000

docker-compose.ymlの作成

  • volumesの設定など、何かとdocker-composeにしておいた方がいいのでdocker-compose.ymlを作成
  • tty: trueとしておくことで、コンテナをずっと起動状態にできる
  • build: .でDockerfileの位置を指定
  • volumes:の指定で、コンテナ内の/appをホスト側にもマウント
version: '3'
services:
  app:
    build: .
    tty: true
    container_name: node-sample01
    volumes:
      - ./app:/app
    ports:
      - "8080:3000"

Webアプリサンプル

ディレクトリ構成

  • ディレクトリ全体構成は以下の通り
app/
 ├ public/
 │ └ 静的なファイル群
 ├ routes
 │ └ index.js
 ├ views
 │ └ index.ejs
 └ app.js
Dockerfile
docker-compose.yml

app.js

var express = require('express');
var app = express();

// テンプレートエンジンをEJSに設定
app.set('views', './views');
app.set('view engine', 'ejs');

// public配下の静的ファイルは無条件に公開
app.use('/public', express.static('public'));

// URLと処理をマッピング
app.use('/', require('./routes/index.js'));

// ポート3000で起動
app.listen(3000);

// アプリケーション開始時のログ
console.log('Server running at http://localhost:3000');

index.js

var express = require('express');
var router = express.Router();

// デフォルトルーティング
router.get('/', function (request, response) {
    // パラメータに値を設定、設定したパラメータはejs内で参照可能となる
    response.render('index', { title: 'NodeSample01', message: 'Hello Node.js' });
});

module.exports = router;

index.ejs

  • index.jsで設定したtitlemessageをejs内で参照
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title><%= title %></title>
    <link rel="stylesheet" href="/public/bootstrap/bootstrap.css" />
    <link rel="stylesheet" href="/public/css/index.css" />
    <script type="text/javascript" src="/public/jquery/jquery.js"></script>
    <script type="text/javascript" src="/public/bootstrap/bootstrap.js"></script>
    <script type="text/javascript" src="/public/js/index.js"></script>
  </head>
  <body>
    <nav class="navbar navbar-inverse navbar-fixed-top">
      <div class="container">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="#">Hello Project</a>
        </div>
      </div>
    </nav>

    <div class="container">
      <div class="starter-template">
        <p><%= message %></p>
        <p><img id="img" src="/public/images/drum.jpg" class="img-thumbnail"></p>
      </div>
    </div> 
  </body>
</html>

起動方法

  • docker-compose upにてコンテナを起動したら、以下でコンテナにログイン
docker exec -it node-sample01 /bin/ash
  • コンテナ内でNode.jsを起動
node app.js

サンプル画面の表示

  • http://localhost:8080で以下画面が表示される NodeSample01.png
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Jest+CircleCIなプロジェクトにCodeCov(カバレッジレポート)を導入するまでの手順ハンズオン

概要

  • テストのコードカバレッジのレポートにCodeCovを使いバッジimage.pngをゲットするまでのハンズオンメモです

開発環境と構成

  • 開発環境
開発言語 JavaScript(ES6)/Node.js
テストフレームワーク Jest
Gitホスティング GitHub
CIツール CircleCI
カバレッジレポート CodeCov
  • 構成
    全体としてはざっくり以下のような構成となります

image.png

CodeCovのサインアップとプロジェクト設定

  1. codecovを開く

まず、CodeCovのサインアップから。

image.png

2.プロジェクトはGithubにホストしているのでGithubを選択

image.png

3.Githubへのアクセス認可の画面がでるので、Authorize CodeDevをクリック

image.png

4.CodeCovの初期画面がでる。

リポジトリがまだ登録されていないのでAdd Repositoryをクリック。

image.png

5.Choose a new repository belowという画面がでてレポジトリ一覧が表示される。

「ここで一覧から対象のリポジトリを選択すれば良し」と思ったら、、、すべてのリポジトリが表示されていない模様

image.png

6.直接リポジトリを指定する

image.png

↑のようなメッセージがでていたので、直接指定することにする。

https://github.com/riversun/event-listener-helperというリポジトリを設定したかったので、上のフォーマットにならい、https://codecov.io/gh/riversun/event-listener-helperにアクセスしたら、そのプロジェクトの初期画面が表示される。

image.png

7.CodeCovのトークンをメモする

初期画面に表示されているトークンをメモしておく。
あとでCircleCIの環境変数に登録する。

環境変数名はCODECOV_TOKENとなる。

CircleCI用のconfig.ymlにCodeCov関係の設定を追記する

CodeCov用のOrbがあるので、その利用を前提としてconfig.ymlを編集する

ちなみに、Orbsとは

Orbs とは
CircleCI Orbs は、ジョブ、コマンド、Executor のような設定要素をまとめた共有可能なパッケージです。 CircleCI 独自の認証済み Orbs のほか、パートナー企業によるサードパーティ製 Orbs を用意しています。

(出典:https://circleci.com/docs/ja/2.0/orb-intro/)

config.ymlを以下のようにした

version: 2.1
orbs:
  node: circleci/node@1.1.6
  codecov: codecov/codecov@1.0.5
jobs:
  build-and-test:
    executor:
      name: node/default
    steps:
      - checkout
      - node/with-cache:
          steps:
            - run: npm install
            - run: npm test
            - codecov/upload:
                file: ./coverage/lcov.info
workflows:
  build-and-test:
    jobs:
      - build-and-test

ポイント

  • orbscodecov: codecov/codecov@1.0.5を追加する

  • カバレッジレポートファイルのアップロードをするためのコマンドcodecov/uploadを以下のように追記する。

config.yml(抜粋)
- codecov/upload:
    file: ./coverage/lcov.info
  • 実際なにやってるか
    • codecov/uploadの内部処理ではcurlコマンドでカバレッジレポートファイルlcov.infoCircleCI(docker)→CodeCovに送信している
    • lcov.info(カバレッジレポートが格納されたファイル)は[root]/coverage/lcov.infoに生成される前提。そのように生成されるようにJestの設定(jest.config.js)を次でする

Jestの設定

念のためにJestの設定をみておく。

やりたいことは以下の2点

  • Jestでテストするときにコードカバレッジも計測するようにすること
  • コードカバレッジのレポートが目的の場所に生成されるようにすること

ということでjest.config.jsの設定をまるっと掲載すると以下のようになる

jest.config.js
module.exports = {
  verbose: true,
  testEnvironment: 'jsdom',
  testMatch: [
    "**/test/**/*.test.js"
  ],
  testTimeout: 5000,
  moduleDirectories: [
    "node_modules"
  ],
  transformIgnorePatterns: [
    "node_modules/(?!(@riversun/event-emitter)/)"
  ],
  "coverageDirectory": "./coverage/",
  "collectCoverage": true
};

本稿で重要なところは以下の部分

jest.config.js(抜粋)
 "coverageDirectory": "./coverage/",
  "collectCoverage": true

coverageDirectoryでカバレッジ計測レポートの関連ファイルの生成先を指定する
collectCoverage:trueでテストする毎にカバレッジ計測レポートが生成されるようになる
(jestコマンドに --coverageオプションをつけて、必要なときに生成するアプローチでもアリ)

CircleCIでプロジェクトをセットアップする

さてここからCircleCIでプロジェクトのセットアップをしていく。

1. https://circleci.com/ を開いてGo to Appクリック(CircleCI自体のサインアップは割愛)

image.png

2.サードパーティ製のOrbsをつかえるようにする

さきほど、CodeCovOrbを使う記述をconfig.ymlに記述したが、サードパーティ製のOrbsを使うための設定を1度だけやっておく必要がある。

画面左の歯車アイコンをクリックすると、

image.png

Organization Settingsという画面がでるので、Allow uncertified orbsYesにすることで、サードパーティ製Orbsの使用を許可できる。

image.png

この設定はプロジェクト横断設定なので1度だけやればOK。

3.プロジェクトを追加する

さて、サードパーティ製のOrbsの許可までできたので、ここでCIしたいプロジェクトを登録する。

以下の画面で、Add projectをクリック

image.png

↑の画面じゃなくて、↓の画面の場合もある(↑のほうは新UIのプレビュー版で↓は現行UIという位置づけかな?いずれにせよAdd projectをクリックする。)

image.png

自分のプロジェクト一覧からCircleCIしたいプロジェクトのところにあるSet Up Projectをクリック

image.png

3.CircleCI用のConfigを追加する

ここでは、CircleCIが「自動でConfig作りましょうか?」とたずねてくれるが、自前のconfig.ymlを作ってPushしてあるので、Add Manuallyをクリック。

image.png

4.さっそくビルド

既に「circleci/config.ymlがあるならビルド開始できますよ」というメッセージが出るのでStart Buildingをクリックする。

image.png

5.予定通りビルド失敗

そして、CircleCI上にて、ビルドが失敗する。

image.png

CodeCovの環境変数を設定していないので最初のビルドは失敗した。

6.CodeCov用の環境変数を設定する

さきほどメモしたCodeCovの環境変数を、CircleCIにセットする。

現行UIでは以下のようにプロジェクト名の右にある歯車アイコンをクリックする

image.png

Environment Variablesを選択して、Add variableをクリック

image.png

ここで環境変数をセットできるので、環境変数名としてNameCODECOV_TOKENをセット、ValueにはさきほどCodeCovから取得した値をセットしてAdd Variable*をクリック

image.png

無事に環境変数をセットできた模様

image.png

ちなみに、
CircleCIの新UI(プレビュー版)のほうだと、同様の操作は以下のようになる。

対象のプロジェクトのPipelines画面で、Project Settingsをクリック

image.png

Environemnt Variablesを選択して、Add Variablesをクリックする

image.png

(あ、なるほど。UIレイアウト、デザインは違うけどラベルが一緒なので文字だけのドキュメンテーションにしたら同じ説明で済むようにできてますね)

CircleCIでビルドして、カバレッジレポートをCodeCovでみる

1.CircleCIでビルドする

ここまでで、CircleCIでビルドする準備ができたので、あとはコミット・プッシュするなり、ReRunするなりしてCircleCIを回すだけ。

以下のようにビルドが無事成功した模様。

image.png

2.CodeCovでカバレッジレポートを確認する

CodeCovを開き https://codecov.io/gh
さきほど登録したプロジェクトをクリック

image.png

カバレッジレポートが表示された

image.png

Readme.mdにカバレッジ計測バッジをはりつける

CodeCovのプロジェクト画面でSettingsタブでBadgeを選択すると、以下のようにバッジ用のコードがあるので、GithubのReadmeにバッジをつけたいときはMarkdownの内容をコピーして、それをReadme.mdにペーストすればできあがり。

image.png

ReadmeにCodeCovのバッジimage.pngが無事表示された
(本プロジェクトはこちら)

image.png

まとめ

  • 以下のようなJest,CircleCI,CodeCovの組み合わせの導入手順をご紹介しました

image.png

  • 便利でMotivationalなクラウドサービス群に感謝感謝!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Netlify Functions でジョークアクセスカウンターをつくりました

はじめに

既にホスティングサービスが終了している懐かしの「ジオシティーズ」ですが、スケジュール上では 2020/3/31 に全ファイルの削除が行われるようです。

自分にはジオシティーズ上で 2005年くらいまで更新していたサイトがありましたので、FTP でファイルを救出し Netlify の無料枠にて記念に再ホストすることにしました。

…できたものの何かが足りない。

2020-03-30_01-19.png

アクセスカウンターだ!

ということで Netlify に備わる Lambda なサービス Netlify Functions でアクセスカウンターをふとつくってみることにしました。

お気づきのように、Lambda のインスタンスが寝てしまうとカウンターが「飛ぶ」お遊びアクセスカウンターとなっていますが、現代風に JSON や SVG などを使って表示するようになっています。

Netlify Functions について

Netlify Functions は静的ファイルホスティングサービス Netlify が提供する、くだけて言えば Amazon AWS Lambda に対するプログラムの自動デプロイの仕組みで、Netlify のアカウントにて(クレジットカード登録が必要な AWS アカウントなしに)Lambda を利用することができます。

この投稿で分かること

アクセスカウンターの動きはどうだろうという感じですが、いくつかの処理が参考になるかもしれないと Qiita に投稿することにしました。何か使える部分があったら幸いです!

  • Lambda でファイルシステムを操作する
  • netlify-lambda で追加の webpack.config を構成する
  • Lambda で jsdom を使って DOM を操作する

ソースコードと動作デモ

ここで使われているソースコードを github にコミットしてあります。ソース全容を確認しながら読んでいただくと分かりやすいかも知れません。

ソースコード一式

Netlify Functions Template. Includes joke access counter sample program.

https://github.com/h1romas4/netlify-functions-template

動作デモ(しばらくアクセスがないとカウントが 1 に戻ります)

https://maple4estry.netlify.com/

Lambda でファイルシステムを操作する

Netlify Functions(Lambda) でも nodejsの fs オブジェクトを使ってファイルを操作することが出来ます。

fs で作成を行ったファイルは、この「アクセスカウンター」の動きどおり、インスタンスのリビルドなどのタイミングで消されますし、スケールした場合は値が分裂しますので、アルゴリズムとしてステートの性質をあてにしてはいけません。

API へのアクセス数をカウントする JSON ファイルを /tmp に書き込む処理抜粋:

import fs from 'fs';
import * as common from '../common'

// async await 版の fs オブジェクト
const fsp = fs.promises;

// カウンターファイル JSON 出力パス
const counterJsonFile = common.tmp + "/counter.json";

/**
 * Lambda エンドポイント
 */
exports.handler = async (event) => {
    const now = new Date();
    let counterJson = {
        "createDate": now,
        "updateDate": now,
        "count": 1
    };
    try {
        // カウンターファイル存在確認
        const data = await fsp.readFile(counterJsonFile);
        // カウンターファイルが存在すれば JSON 解析してカウンターを更新
        counterJson = JSON.parse(data.toString('utf-8'));
        counterJson.updateDate = now;
        counterJson.count++;
    } catch(e) {
        // カウンターファイルがなければ例外を潰して新規作成
    }
    // カウンターファイル書き込み
    await fsp.writeFile(counterJsonFile, JSON.stringify(counterJson));
    // ...
}

Lambda のハンドラーを exports.handler = async (event)async として fs の操作は fs.promises からもらったオブジェクトで await してあげると簡単でした。

ちなみにこの処理は readFile から writeFile までに間がありますのでアトミックなカウントアップはできません。また nodejs の fs の実装を確認していませんが、ジオシティーズ世代の CGI カウンターよろしく壊れる可能性もあるでしょうか?(懐かしい)。

さて、/tmp は Lambda で使えるテンポラリー領域となっていますが、開発を行うローカル環境でそのまま /tmp にかかれると確認などが面倒なためちょっと小細工をしています。

AWS で設定される環境変数の有無で /tmp の位置を切り替え:

// カウンターファイルを作成するテンポラリーディレクトリ
export let tmp = '/tmp'
// AWS Lambda で動いていない場合はテンポラリーを dist/tmp に設定
if(!("AWS_REGION" in process.env)) {
    tmp = "./dist/tmp"
    try {
        fs.mkdirSync(tmp);
    } catch(e) { }
}

Netlify Functions から提供されている netlify-lambda SDK が ./dist/api 以下にビルドを出力するため、合わせてテンポラリー領域を ./dist/tmp に設定しています。

netlify-lambda で追加の webpack.config を構成する

netlify-lambda SDK は内部的に webpack を使ってビルドしていますが、標準の webpack 設定をマージできる --config オプションが準備されています。

netlify-lambda --config ./webpack.functions.js build src/functions/endpoint

本アクセスカウンターでは、古では GIF 画像などで処理していたカウント画像に代わり、SVG/CSS/HTML を Lambda 上で処理し数字を描いてクライアントに返却したく、処理前の .html を文字列として import するため raw-loader を webpack に追加設定しています。

// webpack.functions.js
module.exports = {
    optimization: { minimize: false },
    module: {
        rules: [
            {
                test: /\.html$/i,
                exclude: /node_modules/,
                use: 'raw-loader'
            }
        ],
    }
};

この設定によりプログラムから html 文字列変数として import ができます。

// counter.js
import html from '../resources/fujilcd.html'

このような追加の webpack 設定がある場合は --config オプションを活用すると良さそうです。

なお、Lambda 上にデプロイするソースをミニマイズしてもあまり意味がないため、どのプロジェクトでもこのコンフィグで optimization: { minimize: false }, を入れておくとビルド時間短縮に役立つかもしれません。

Lambda で jsdom を使って DOM を操作する

nodejs 上で html などの DOM 操作を行う jsdom パッケージを Netlify Functions(Lambda?) 上で使う場合は少々コツが必要なようです。

通常通り packege.json に依存関係をいれると、

Error while initializing entrypoint: { Error: Cannot find module 'canvas'

というエラーで canvas パッケージを依存に入れても動作しませんでした。 issue を探ったところワークアラウンドがありましたので、この方法で回避しています。

https://github.com/jsdom/jsdom/issues/1708#issuecomment-462990288

I was able to work around this by adding "canvas": "file:./canvas" to the dependencies section of the app's package.json and creating canvas/index.js containing simply module.exports = {}.

プロジェクト上に空の canvas モジュールをつくって依存に追加:

// package.json
{
    "dependencies": {
        "jsdom": "^16.2.1",
        "canvas": "file:./src/functions/canvas",
        "utf-8-validate": "^5.0.2",
        "bufferutil": "^4.0.1"
    },
}

jsdom が導入できれば、先程 import したような文字列 HTML を new JSDOM(html) で DOM 化しウェブブラウザーと同様に document.querySelector などで操作することができます。

import したカウンター表示 HTMLを DOM 操作して CSS クラスを付けてカウント数を表示:

// counter.js
function updateLCD(html, number) {
    const dom = new JSDOM(html);
    const { document } = dom.window;

    let countString = number + ""
    countString = ("0".repeat(maxCount) + number);
    countString = countString.substring(countString.length - maxCount)

    for(let i = 0; i < countString.length; i++) {
        let number = countString.substring(i, i + 1);
        const digi = document.querySelector(`.digit-${i} svg`);
        digi.removeAttribute('class');
        digi.classList.add(`num-${number}`);
    }

    return dom.serialize();
}

jsdomdom.serialize();にてできた DOM を文字列化できますので、これをアクセスしてきたウェブブラウザーに返却し、

// counter.js
exports.handler = async (event) => {
    // ...
    // カウンター数 SVG を生成
    counterJson.html = updateLCD(html, counterJson.count);
    return {
        statusCode: 200,
        headers: {
            "Content-Type": "application/json; charset=utf-8",
        },
        body: JSON.stringify(counterJson)
    }
};

ウェブブラウザーは shadowDOM で画面にそのまま書き出しています。

/v1/counterとして Netlify Functions にデプロイした Lambda を呼び出すクライアントのソース:

<!DOCTYPE html>
<html>
<body>
<access-counter></access-counter>

<script>
  fetch('/v1/counter').then(function(response) {
    return response.json()
  }).then(function(json) {
    customElements.define('access-counter',
      class extends HTMLElement {
        constructor() {
          super();
          const shadowRoot = this.attachShadow({ mode: 'open' });
          shadowRoot.innerHTML = json.html;
      }
    })
  });
</script>
</body>
</html>

終わりに

現代版ジオシティーズとも言える Netlify を今回初めて使ってみたのですが、Netlify Functions 含めいろいろできて便利でした。

試しに検索エンジン API もつくってみましたので、自分のブログになってしまっていますが、気になる方がもしいらっしゃいましたらご覧ください!

Netlify Functions で検索エンジン API をつくる

ふと思いつきまして Netlify で使える Lambda なサービス、Netlify Functions を用いて参照系検索 API を作成してみました。無料枠での挑戦です。

netlify-01.jpeg

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

Netlify Functions で古のアクセスカウンター(アクセサリー)をつくる

はじめに

既にホスティングサービスが終了している懐かしの「ジオシティーズ」ですが、スケジュール上では 2020/3/31 に全ファイルの削除が行われるようです。

自分にはジオシティーズ上で 2005年くらいまで更新していたサイトがありましたので、FTP でファイルを救出し Netlify の無料枠にて記念に再ホストすることにしました。

…できたものの何かが足りない。

2020-03-30_01-19.png

アクセスカウンターだ!

ということで Netlify に備わる Lambda なサービス Netlify Functions でアクセスカウンターをふとつくってみることにしました。

お気づきのように、Lambda のインスタンスが寝てしまうとカウンターが「飛ぶ」アクセスカウンター(アクセサリー)となっていますが、現代風に JSON や SVG などを使って処理するようになっています。

Netlify Functions について

Netlify Functions は静的ファイルホスティングサービス Netlify が提供する、くだけて言えば Amazon AWS Lambda に対するプログラムの自動デプロイの仕組みで、Netlify のアカウントにて(クレジットカード登録が必要な AWS アカウントなしに)Lambda を利用することができます。

この記事で分かること

アクセスカウンターの動きはどうだろうという感じですが、いくつかの処理が参考になるかもしれないと Qiita に投稿することにしました。何か使える部分があったら幸いです。

  • Lambda でファイルシステムを操作する
  • netlify-lambda で追加の webpack.config を構成する
  • Lambda で jsdom を使って DOM を操作する

ソースコードと動作デモ

ここで使われているソースコードを github にコミットしてあります。ソース全容を確認しながら読んでいただくと分かりやすいかも知れません。

ソースコード一式

Netlify Functions Template. Includes joke access counter sample program.

https://github.com/h1romas4/netlify-functions-template

動作デモ(しばらくアクセスがないとカウントが 1 に戻ります)

https://maple4estry.netlify.com/

Lambda でファイルシステムを操作する

Netlify Functions(Lambda) でも nodejsの fs オブジェクトを使ってファイルを操作することが出来ます。

fs で作成を行ったファイルは、この「アクセスカウンター」の動きどおり、インスタンスのリビルドなどのタイミングで消されますし、スケールした場合は値が分裂しますので、アルゴリズムとしてステートの性質をあてにしてはいけません。

API へのアクセス数をカウントする JSON ファイルを /tmp に書き込む処理抜粋:

import fs from 'fs';
import * as common from '../common'

// async await 版の fs オブジェクト
const fsp = fs.promises;

// カウンターファイル JSON 出力パス
const counterJsonFile = common.tmp + "/counter.json";

/**
 * Lambda エンドポイント
 */
exports.handler = async (event) => {
    const now = new Date();
    let counterJson = {
        "createDate": now,
        "updateDate": now,
        "count": 1
    };
    try {
        // カウンターファイル存在確認
        const data = await fsp.readFile(counterJsonFile);
        // カウンターファイルが存在すれば JSON 解析してカウンターを更新
        counterJson = JSON.parse(data.toString('utf-8'));
        counterJson.updateDate = now;
        counterJson.count++;
    } catch(e) {
        // カウンターファイルがなければ例外を潰して新規作成
    }
    // カウンターファイル書き込み
    await fsp.writeFile(counterJsonFile, JSON.stringify(counterJson));
    // ...
}

Lambda のハンドラーを exports.handler = async (event)async として fs の操作は fs.promises からもらったオブジェクトで await してあげると簡単でした。

ちなみにこの処理は readFile から writeFile までに間がありますのでアトミックなカウントアップはできません。また nodejs の fs の実装を確認していませんが、ジオシティーズ世代の CGI カウンターよろしく壊れる可能性もあるでしょうか?(懐かしい)。

さて、/tmp は Lambda で使えるテンポラリー領域となっていますが、開発を行うローカル環境でそのまま /tmp にかかれると確認などが面倒なためちょっと小細工をしています。

AWS で設定される環境変数の有無で /tmp の位置を切り替え:

// カウンターファイルを作成するテンポラリーディレクトリ
export let tmp = '/tmp'
// AWS Lambda で動いていない場合はテンポラリーを dist/tmp に設定
if(!("AWS_REGION" in process.env)) {
    tmp = "./dist/tmp"
    try {
        fs.mkdirSync(tmp);
    } catch(e) { }
}

Netlify Functions から提供されている netlify-lambda SDK が ./dist/api 以下にビルドを出力するため、合わせてテンポラリー領域を ./dist/tmp に設定しています。

netlify-lambda で追加の webpack.config を構成する

netlify-lambda SDK は内部的に webpack を使ってビルドしていますが、標準の webpack 設定をマージできる --config オプションが準備されています。

netlify-lambda --config ./webpack.functions.js build src/functions/endpoint

本アクセスカウンターでは、古では GIF 画像などで処理していたカウント画像に代わり、SVG/CSS/HTML を Lambda 上で処理し数字を描いてクライアントに返却したく、処理前の .html を文字列として import するため raw-loader を webpack に追加設定しています。

// webpack.functions.js
module.exports = {
    optimization: { minimize: false },
    module: {
        rules: [
            {
                test: /\.html$/i,
                exclude: /node_modules/,
                use: 'raw-loader'
            }
        ],
    }
};

この設定によりプログラムから html 文字列変数として import ができます。

// counter.js
import html from '../resources/fujilcd.html'

このような追加の webpack 設定がある場合は --config オプションを活用すると良さそうです。

なお、Lambda 上にデプロイするソースをミニマイズしてもあまり意味がないため、どのプロジェクトでもこのコンフィグで optimization: { minimize: false }, を入れておくとビルド時間短縮に役立つかもしれません。

Lambda で jsdom を使って DOM を操作する

nodejs 上で html などの DOM 操作を行う jsdom パッケージを Netlify Functions(Lambda?) 上で使う場合は少々コツが必要なようです。

通常通り packege.json に依存関係をいれると、

Error while initializing entrypoint: { Error: Cannot find module 'canvas'

というエラーで canvas パッケージを依存に入れても動作しませんでした。 issue を探ったところワークアラウンドがありましたので、この方法で回避しています。

https://github.com/jsdom/jsdom/issues/1708#issuecomment-462990288

I was able to work around this by adding "canvas": "file:./canvas" to the dependencies section of the app's package.json and creating canvas/index.js containing simply module.exports = {}.

プロジェクト上に空の canvas モジュールをつくって依存に追加:

// package.json
{
    "dependencies": {
        "jsdom": "^16.2.1",
        "canvas": "file:./src/functions/canvas",
        "utf-8-validate": "^5.0.2",
        "bufferutil": "^4.0.1"
    },
}

jsdom が導入できれば、先程 import したような文字列 HTML を new JSDOM(html) で DOM 化しウェブブラウザーと同様に document.querySelector などで操作することができます。

import したカウンター表示 HTMLを DOM 操作して CSS クラスを付けてカウント数を表示:

// counter.js
function updateLCD(html, number) {
    const dom = new JSDOM(html);
    const { document } = dom.window;

    let countString = number + ""
    countString = ("0".repeat(maxCount) + number);
    countString = countString.substring(countString.length - maxCount)

    for(let i = 0; i < countString.length; i++) {
        let number = countString.substring(i, i + 1);
        const digi = document.querySelector(`.digit-${i} svg`);
        digi.removeAttribute('class');
        digi.classList.add(`num-${number}`);
    }

    return dom.serialize();
}

jsdomdom.serialize();にてできた DOM を文字列化できますので、これをアクセスしてきたウェブブラウザーに返却し、

// counter.js
exports.handler = async (event) => {
    // ...
    // カウンター数 SVG を生成
    counterJson.html = updateLCD(html, counterJson.count);
    return {
        statusCode: 200,
        headers: {
            "Content-Type": "application/json; charset=utf-8",
        },
        body: JSON.stringify(counterJson)
    }
};

ウェブブラウザーは shadowDOM で画面にそのまま書き出しています。

/v1/counterとして Netlify Functions にデプロイした Lambda を呼び出すクライアントのソース:

<!DOCTYPE html>
<html>
<body>
<access-counter></access-counter>

<script>
  fetch('/v1/counter').then(function(response) {
    return response.json()
  }).then(function(json) {
    customElements.define('access-counter',
      class extends HTMLElement {
        constructor() {
          super();
          const shadowRoot = this.attachShadow({ mode: 'open' });
          shadowRoot.innerHTML = json.html;
      }
    })
  });
</script>
</body>
</html>

終わりに

現代版ジオシティーズとも言える Netlify を今回初めて使ってみたのですが、Netlify Functions 含めいろいろできて便利でした。

試しに検索エンジン API もつくってみましたので、自分のブログになってしまっていますが、気になる方がもしいらっしゃいましたらご覧ください!

Netlify Functions で検索エンジン API をつくる

ふと思いつきまして Netlify で使える Lambda なサービス、Netlify Functions を用いて参照系検索 API を作成してみました。無料枠での挑戦です。

netlify-01.jpeg

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

Mac HomebrewでNode.jsをインストールする

目的

  • Mac端末にHomebrewを用いてNode.jsをインストールする方法をまとめる

実施環境

  • ハードウェア環境
項目 情報 備考
OS macOS Catalina(10.15.3)
ハードウェア MacBook Air (11-inch ,2012)
プロセッサ 1.7 GHz デュアルコアIntel Core i5
メモリ 8 GB 1600 MHz DDR3
グラフィックス Intel HD Graphics 4000 1536 MB

実施条件

  • Homebrewが使用できる状態になっていること。

実施方法概要

  1. Nodebrewのインストール
  2. Node.jsのインストール
  3. 設定

実施方法詳細

  1. Nodebrewのインストール

    1. 下記コマンド実行してNodebrewをインストールする。

      $ brew install nodebrew
      
    2. 下記コマンドを実行してNodebrewのインストールを確認する。

      nodebrew -v
      
  2. Node.jsのインストール

    1. 下記コマンドを実行して最新の安定バージョンをインストールする。(エラーが出た方はこちら→Mac Nodebrewを用いてNode.jsをインストールした時にエラーが出た話)

      $ nodebrew install-binary stable
      
    2. 下記コマンドを実行してNode.jsのインストールを確認する。

      $ nodebrew --version
      
  3. 設定

    1. 先に実行したコマンド$ nodebrew --versionの出力の「# use a specific version number」の下部に記載されているコマンドを実行する。(「vX.X.X is not installed」というエラーが出た方はこちら→Mac Node.jsのバージョン有効化する手順でvX.X.X is not installedと出力された話)(X.X.Xはバージョン名)

      $ nodebrew use vX.X.X
      >use vX.X.X
      
    2. 下記コマンドを実行してインストールしたNode.jsのバージョンが有効化されている事を確認する。

      $ nodebrew ls
      ・
      ・
      ・
      >current: vX.X.X
      
    3. 先にインストールしたバージョンが「current:」の後に記載されていれば導入完了である。

    4. 下記コマンドを実行してパスを通す。

      $ echo 'export PATH=$PATH:~/.nodebrew/current/bin/' >> ~/.bashrc
      
    5. 「.bashrc」ファイルがログイン時に読み込まれるように設定していない場合は下記の手順を実施する。

      1. 下記コマンドを実行して「.bash_profile」ファイルを開く

        $ vi ~/.bash_profile
        
      2. 下記の内容を追記する。

        ~/.bash_profile
        source ~/.bashrc
        
    6. 下記コマンドを実行して「.bashrc」ファイルを読み込む

      $ source ~/.bashrc
      
    7. 下記コマンド実行してパスが通っている事を確認する(command not foundにならなければOK)

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