20200110のNode.jsに関する記事は7件です。

Windows10で快適な開発環境を得るための道のり~Node.js編~

はじめに

環境

  • WIndows 10 Home
  • WSL2
  • Ubuntu 18.04

導入手順

node.js, npmをインストールする.

$ sudo apt install -y nodejs npm

再ログイン後,バージョンの確認.(なぜかnodejsでコマンドで登録される.)

$ nodejs --version
v8.10.0

古い..

安定版の最新バージョンを使用したいのでn packageを導入する.

$ sudo npm install n --global
/usr/local/bin/n -> /usr/local/lib/node_modules/n/bin/n
/usr/local/lib
└── n@6.1.3

※n packageについては,こちらで詳しく紹介されています. → Node.jsのバージョンを管理するライブラリ「n」

n packageを用いて,安定版の最新バージョンをインストールする.

$ sudo n stable

  installing : node-v12.14.1
       mkdir : /usr/local/n/versions/node/12.14.1
       fetch : https://nodejs.org/dist/v12.14.1/node-v12.14.1-linux-x64.tar.gz
   installed : v12.14.1 (with npm 6.13.4)

Note: the node command changed location and the old location may be remembered in your current shell.
         old : /usr/bin/node
         new : /usr/local/bin/node
To reset the command location hash either start a new shell, or execute PATH="$PATH"

最初に入れた古いnode.js, npmは削除する.

$ sudo apu purge -y nodejs npm
$ exec $SHELL -l

バージョンの確認

$ node --version
v12.14.1

→ OK!

参考

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

HomebrewでMySQL8を入れたが、node.jsから接続できない(認証方式変更)

エラー内容

Client does not support authentication protocol requested by server; consider upgrading MySQL client

原因

MySQL8から認証方式が変わった(caching_sha2_password)ようで、
Homebrewからインストールできるmysqlはその認証方式に対応していないのが原因っぽい。

やったこと

パスワードのポリシーを確認

SHOW VARIABLES LIKE 'validate_password%';

認証方式の変更

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';

反映

flush privileges;

確認

SELECT user, host, plugin FROM mysql.user;

 2020-01-10 16.39.54.jpg

参考

https://stackoverflow.com/questions/50093144/mysql-8-0-client-does-not-support-authentication-protocol-requested-by-server
https://its-office.jp/blog/web/2019/02/13/mysql8.html
https://qiita.com/ucan-lab/items/3ae911b7e13287a5b917

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

ServerlessFramework+Slackで運行遅延情報をお知らせする

はじめに

AWSLambdaで運行遅延情報をslackに通知するbotを作りました。
運行遅延APIを利用して、遅延が発生して入れば運行会社のWEBから遅延内容をスクレイピングしてSlackにお知らせします。
何番煎じか分かりませんが、lambdaとnodejsで書かれた記事が見当たらなかったので紹介します。

作ったもの

スクリーンショット 2020-01-09 16.40.55.png

GitHub
https://github.com/t-yasukawa/incoming-webhook

環境

  • macOS Catalina 10.15.2
  • VSCode 1.41.1
  • AWS(Lambda, CloudFormation)
  • ServerlessFramework 1.60
  • Node.js 12.14.1
  • puppeteer 2.0.0
  • chrome-aws-lambda 2.0.0

事前準備

1. ServerlessFrameworkの導入

まずはバージョン確認

$ npm ls --depth=0 -g
/Users/t-yasukawa/.nodebrew/node/v12.14.1/lib
└── npm@6.13.4

ServerlessFrameworkを入れます。
npm i serverless -g でも良いのですが、他のプロジェクトでも使っているので今回はプロジェクト直下におきます。
npm init で生成される package.jsonの初期値は適当に埋めてください。

$ mkdir incoming-webhook
$ cd incoming-webhook
$ npm init
package name: (incoming-webhook) 
version: (1.0.0) 
description: 
entry point: (index.js) 
test command: 
git repository: 
keywords: 
author: 
license: (ISC) 

$ npm i serverless
$ npm ls --depth=0
incoming-webhook@1.0.0 /Users/t-yasukawa/git/incoming-webhook
└── serverless@1.60.5

無事インストールできました。
しかしこのままでは ./node_modules/.bin/sls と毎回打たないといけないので面倒です。
方法は色々ありますが、安直にパスを追加します。

$ echo 'export PATH=node_modules/.bin:$PATH' >> ~/.bash_profile
$ source ~/.bash_profile
$ sls -v
Framework Core: 1.60.5
Plugin: 3.2.7
SDK: 2.2.1
Components Core: 1.1.2
Components CLI: 1.4.0

これでOKです。
早速プロジェクトファイルをテンプレートから作成します。

$ sls create --template aws-nodejs --path ./
Serverless: Generating boilerplate...

  Serverless Error ---------------------------------------

  The directory "/Users/t-yasukawa/git/incoming-webhook/" already exists, and serverless will not overwrite it. Rename or move the directory and try again if you want serverless to create it"

はい、怒られました。
プロジェクトディレクトリはこのタイミングで作らないといけないようです。
serverlessをグローバルにしなかったせいですね。仕方ないので一旦パスを変えます。

$ sls create --template aws-nodejs --path ./src/models/lambda
Serverless: Generating boilerplate...
Serverless: Generating boilerplate in "/Users/t-yasukawa/git/test/src/models/lambda"
 _______                             __
|   _   .-----.----.--.--.-----.----|  .-----.-----.-----.
|   |___|  -__|   _|  |  |  -__|   _|  |  -__|__ --|__ --|
|____   |_____|__|  \___/|_____|__| |__|_____|_____|_____|
|   |   |             The Serverless Application Framework
|       |                           serverless.com, v1.60.5
 -------'

Serverless: Successfully generated boilerplate for template: "aws-nodejs"

生成されたファイルを変更します。

$ mv src/models/lambda/serverless.yml ./
$ mv src/models/lambda/.gitignore ./
$ tree -a -L 1
.
├── .gitignore
├── node_modules
├── package-lock.json
├── package.json
├── serverless.yml
└── src

2.puppeteer導入

スクレイピング処理にpuppeteerを利用しますが、このままローカルで利用することができたのですが、
いざLambda上にデプロイしようとした時にLambdaの上限250MBを超えてしまう問題が発生しました。
An error occurred: SessionLambdaFunction - Unzipped size must be smaller than 262144000 bytes.

そこで、軽量版の puppeteer-core とAWS上でchromiumが動く chrome-aws-lambda を入れることでこれを回避します。
(ついでにAPI取得用のaxiosも入れます。)
※versionを揃えないと実行時にエラーとなるので注意
Error: Chromium revision is not downloaded. Run "npm install" or "yarn install"

$ npm i chrome-aws-lambda puppeteer-core axios
$ npm ls --depth=0
incoming-webhook@1.0.0 incoming-webhook
├── axios@0.19.1
├── chrome-aws-lambda@2.0.0
├── puppeteer-core@2.0.0
└── serverless@1.60.5

3.実装

  • 遅延情報APIで遅延情報を取得
  • お知らせしたい路線を検出
  • 検出できたらWebに飛んでスクレイピング
  • Slackに送信
handler.js
'use strict'

const axios = require("axios")
const chromium = require("chrome-aws-lambda");

// 取得したい路線情報
const CHECK_LIST = [
    {
        'name': '常磐線',
        'company': 'JR東日本',
        'website': 'https://traininfo.jreast.co.jp/train_info/tohoku.aspx',
        'selector': async (page) => await selectorForJrEast(page, '常磐線')
    },
    {
        'name': '東北本線',
        'company': 'JR東日本',
        'website': 'https://traininfo.jreast.co.jp/train_info/tohoku.aspx',
        'selector': async (page) => await selectorForJrEast(page, '東北本線')
    },
    {
        'name': '仙台市営地下鉄',
        'company': '仙台市交通局',
        'website': 'https://www.kotsu.city.sendai.jp/unkou/',
        'selector': async (page) => await selectorForSendaiSubway(page)
    },
]

module.exports.sendToSlack = async () => {
    // 鉄道運行遅延の情報を取得
    const notifyDelays = await getNotifyDelays()
    if (notifyDelays.length == 0) {
        console.log('遅延情報はありませんでした。')
        return;
    }
    console.log('遅延情報が見つかりました。')

    // 遅延内容を取得
    const messages = await getDelayMessage(notifyDelays)
    console.log(messages.join('\n'))

    // Sclackに送信
    await postSlack(messages.join('\n'))
}

/**
 * 遅延情報を取得
 */
async function getNotifyDelays() {
    const delay_url = process.env['TRAIN_DELAY_JSON_URL']
    const notifyDelays = []

    try {
        // 運行遅延情報を取得
        const res = await axios.get(delay_url)
        // res = [{
        //     "name":"東北本線",
        //     "company":"JR東日本",
        //     "lastupdate_gmt":1578638905,
        //     "source":"鉄道com RSS"
        // }]

        // 通知する路線のみ抽出
        res.data.forEach(delayItem => {
            CHECK_LIST.forEach(checkItem => {
                if (delayItem.name == checkItem.name && delayItem.company == checkItem.company) {
                    notifyDelays.push(checkItem)
                }
            })
        })
    } catch (error) {
        console.error(error)
    }

    return notifyDelays
}

/**
 * 遅延メッセージを取得
 * 
 * @param {Array} delays 
 */
async function getDelayMessage(delays) {
    const messages = [];
    let browser = null
    try {
        browser = await chromium.puppeteer.launch({
            args: chromium.args,
            defaultViewport: chromium.defaultViewport,
            executablePath: await chromium.executablePath,
            headless: chromium.headless
          })
        const page = await browser.newPage()

        for(const i of delays) {
            // websiteから遅延情報をスクレイピング
            await page.goto(i.website)
            const detail = await i.selector(page)
            const message = `*・${i.company} \<${i.name}\>* (<${i.website}|jump>)\n ${detail}\n`
            messages.push(message)
        }
    } catch(e) {
        console.warn(e)
    } finally {
        if (browser !== null) {
            await browser.close()
        }
    }

    return messages; 
}

/**
 * JR東日本(東北エリア)の遅延内容をスクレイピング
 * 
 * @param {Page} page Page
 * @param {string} target 路線名
 */
async function selectorForJrEast(page, target) {
    const selector = '#wrapper > div.main_con02 > div.table_access > table > tbody > tr'
    const messages = []
    try {
        for (const item of await page.$$(selector)) {
            const lineName = await getTextContext(item, '.line_name')
            if (lineName == target) {
                const message = await getTextContext(item, '.status_text')
                messages.push(message)
            }
        }
    } catch (error) {
        console.error(error)
        return `:warning: ノードの取得に失敗しました。DOMが変更されている可能性があります。\n  \`${selector}\` `
    }

    return messages.join('\n')
}

/**
 * 仙台市地下鉄(南北・東西)の遅延内容をスクレイピング
 * 
 * @param {Page} page Page
 */
async function selectorForSendaiSubway(page) {
    const selector = '#unkou_detail'

    try {
        const item = await page.$(selector)
        const text = await getTextContext(item)
        if (text == null) {
            return `:warning: ノードの取得に失敗しました。DOMが変更されている可能性があります。\n  \`${selector}'\` `
        }
    } catch (error) {
        console.error(error)
        return `:warning: ノードの取得に失敗しました。DOMが変更されている可能性があります。\n  \`${selector}'\` `
    }

    return text
}

/**
 * textContent取得
 * 
 * @param {ElementHandle} elementHandle 
 * @param {string} target 
 */
async function getTextContext(elementHandle, target) {
    const tag = await elementHandle.$(target)
    const prop = await tag.getProperty('textContent')
    const text = await prop.jsonValue()
    return text
}

/**
 * Slackへ送信
 * 
 * @param {string} message 
 */
async function postSlack(message) {
    const slack_url = process.env['SLACK_WEBHOOK_URL']
    const payload = {
        'username': '運行遅延お知らせbot',
        'icon_emoji': ':train:',
        'attachments': [
            {
                'fallback': message,
                'color': '#36a64f',
                'pretext': '<!channel> 電車の遅延があります。',
                'text': message,
                "mrkdwn_in": [
                    "text"
                ],
                'channel': '#列車運行情報'
            }
        ]
    }

    const res = await axios.post(slack_url, payload)
    console.log(res)
}
serverless.yml
service: incoming-webhook

provider:
  name: aws
  runtime: nodejs12.x
  timeout: 300
  profile: ${self:custom.profiles.${self:provider.stage}}
  region: ${opt:region, self:custom.defaultRegion}
custom:
  defaultRegion: ap-northeast-1
  profiles:
    dev: default

package:
  exclude:
    - node_modules/serverless/**
    - node_modules/chrome-aws-lambda/**
    - chrome-aws-lambda/**

functions:
  sendTrainDelayToSlack:
  handler: src/models/lambda/handler.sendToSlack
  events:
    - schedule: 
      rate: cron(15 9,22,23 ? * MON-FRI *)  ## 7:15,8:15,18:15 月~金 
  layers:
      - {Ref: ChromeLambdaLayer}
  environment:
    TRAIN_DELAY_JSON_URL: 'https://tetsudo.rti-giken.jp/free/delay.json'
    SLACK_WEBHOOK_URL: 'https://hooks.slack.com/services/****************'

layers:
  chrome:
    package:
      artifact: ./chrome-aws-lambda/chrome_aws_lambda.zip 

4. 動作確認(問題発生)

動作確認のためにローカルでLambdaを実行するとchromiumが起動できないとエラーになりました。 :weary:

$ sls invoke local --function sendTrainDelayToSlack
遅延情報が見つかりました。
Error: Failed to launch chrome!
/var/folders/v8/ydzbmvkj6_zbm8msr6x730nr0000gn/T/chromium: /var/folders/v8/ydzbmvkj6_zbm8msr6x730nr0000gn/T/chromium: cannot execute binary file

TROUBLESHOOTING: https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md

Mac環境でデバッグしようとするとPC内のChromeアプリのバイナリを利用しようとするのですが、
バイナリファイルのchromiumを実行することができませんでした。

Chromeとモジュールのバージョンがリビジョン単位で違うけど、そのせい?よくわからず。。
Chromeバージョン: 79.0.3945.117 (2020/01/10時点)

puppeteer Version chrome-aws-lambda Version Chromium Revision
2.0.* npm i chrome-aws-lambda@~2.0.2 705776 (79.0.3945.0)

参照: https://github.com/alixaxel/chrome-aws-lambda

解決策

ローカルでは容量が大きいけど puppeteer でchroniumのバイナリファイルをDLして利用し、
AWS上では puppeteer-corechrome-aws-lambda を使うことにしました。

$ npm i --save-prod chrome-aws-lambda puppeteer-core

$ npm i --save-dev puppeteer
Downloading Chromium r706915 - 111.8 Mb [====================] 100% 0.0s 
Chromium downloaded to /Users/t-yasukawa/git/incoming-webhook/node_modules/puppeteer/.local-chromium/mac-706915

ただ、これだけではローカルで動かないのでデバッグ中だけ executablePath を変える必要があります。

browser = await chromium.puppeteer.launch({
    args: chromium.args,
    defaultViewport: chromium.defaultViewport,
-    executablePath: await chromium.executablePath,
+    executablePath: null,
    headless: chromium.headless
    })

もしくは上記でDLしたchromiumのパスでも行けると思います。

executablePath: `${process.cwd()}/node_modules/puppeteer/.local-chromium/mac-706915/chrome-mac/Chromium.app/Contents/MacOS/Chromium`

5.動作確認(解決)

$ sls invoke local --function sendTrainDelayToSlack
遅延情報が見つかりました。
*・JR東日本 <東北本線>* (<https://traininfo.jreast.co.jp/train_info/tohoku.aspx|jump>)
 東北本線は、釜石線内でのシカと衝突の影響で、盛岡~花巻駅間の上下線で一部列車が運休となっています。

無事スクレイピングできました。・・・シカさん :scream_cat:

6.解説

API関連は axios を使いました。とてもシンプルで使いやすい!

const res = await axios.get(delay_url)

const res = await axios.post(slack_url, payload)

遅延情報はJR東日本と仙台市地下鉄の2サイトからスクレイピングしました。
地下鉄はノード指定ですんなり取得できましたが、JRの方はノード取得に癖があったので力技でした。

スクレイピングのやり方ですが簡単に取得できます。(chromeの場合)
デベロッパーツールを開く(検証モード) → 指定ノードの箇所で右クリック → Copy → Copy selector

スクリーンショット 2020-01-10 11.57.13.png

コンソール上で以下を入力することでも確認できます。
document.querySelector('{copyしたノード}').textContent

jsなのでそのままコードで使えますが、今回は puppeteer の用意したものを使います。

const browser = await chromium.puppeteer.launch()
const page = await browser.newPage()
await page.goto({webUrl})
const tag = await page.$('#wrapper > div.main_con02 > div.table_access > table > tbody > tr:nth-child(21) > td > p.status_text')
const prop = await tag.getProperty('textContent')
const text = await prop.jsonValue()

console.log(text)
// 水郡線は、台風の影響で、西金~常陸大子駅間の上下線で当面の間運転を見合わせます。同区間でバスによる代行輸送を実施します。 

それにしても長い。。。

7.AWSへデプロイ

さぁ、最後はAWSへデプロイしてCloudWatchを使って定時実行させれば完成です。
Lambdaの容量をなるべく節約して使うため不要なモジュールたちを削除します。
ローカルで使っていた puppeteer を除いた状態で再インストールします。
serverless もLambdaには必要ないのですがデプロイコマンドで必要なのでproductionに含めます。

$ rm -rf node_modules
$ npm i --production

chrome-aws-lambda もそこそこの容量なのでそのまま入れずzipに固めてLambda Layerに格納させます。
他のLambdaでスクレイピングしたい時はこのLayerが汎用的に使えて便利です。
READMEにしたがってzipに圧縮します。
パーミッションも変えないとデプロイできなかったので適宜変えてください。

$ git clone --depth=1 https://github.com/alixaxel/chrome-aws-lambda.git
$ cd chrome-aws-lambda
$ make chrome_aws_lambda.zip
$ chmod 777 chrome_aws_lambda.zip

Lambdaアプリをzipで固める前にさらに不要なファイルを除外します。

serverless.yml
package:
  exclude:
    - node_modules/serverless/**
    - node_modules/chrome-aws-lambda/**
    - chrome-aws-lambda/**

layers:
  chrome:
    package:
      artifact: ./chrome-aws-lambda/chrome_aws_lambda.zip 

最後にデプロイして終了!

ちょっとしたアプリですがモジュールを入れるとそこそこのサイズになりますね。
incoming-webhook.zip file to S3 (25.49 MB)
chrome_aws_lambda.zip file to S3 (41.63 MB)

$ sls deploy --verbose --profile dev
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service incoming-webhook.zip file to S3 (25.49 MB)...
Serverless: Uploading service chrome_aws_lambda.zip file to S3 (41.63 MB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
CloudFormation - UPDATE_IN_PROGRESS - AWS::CloudFormation::Stack - incoming-webhook-dev
CloudFormation - UPDATE_IN_PROGRESS - AWS::Lambda::LayerVersion - ChromeLambdaLayer
CloudFormation - UPDATE_IN_PROGRESS - AWS::Lambda::LayerVersion - ChromeLambdaLayer
CloudFormation - UPDATE_COMPLETE - AWS::Lambda::LayerVersion - ChromeLambdaLayer
CloudFormation - UPDATE_IN_PROGRESS - AWS::Lambda::Function - SendTrainDelayToSlackLambdaFunction
CloudFormation - UPDATE_COMPLETE - AWS::Lambda::Function - SendTrainDelayToSlackLambdaFunction
CloudFormation - UPDATE_COMPLETE_CLEANUP_IN_PROGRESS - AWS::CloudFormation::Stack - incoming-webhook-dev
CloudFormation - DELETE_IN_PROGRESS - AWS::Lambda::LayerVersion - ChromeLambdaLayer
CloudFormation - DELETE_COMPLETE - AWS::Lambda::LayerVersion - ChromeLambdaLayer
CloudFormation - UPDATE_COMPLETE - AWS::CloudFormation::Stack - incoming-webhook-dev
Serverless: Stack update finished...
Service Information
service: incoming-webhook
stage: dev
region: ap-northeast-1
stack: incoming-webhook-dev
resources: 7
api keys:
  None
endpoints:
  None
functions:
  sendTrainDelayToSlack: incoming-webhook-dev-sendTrainDelayToSlack
layers:
  chrome: arn:aws:lambda:ap-northeast-1:*:layer:chrome:9

Stack Outputs
SendTrainDelayToSlackLambdaFunctionQualifiedArn: arn:aws:lambda:ap-northeast-1:*:function:incoming-webhook-dev-sendTrainDelayToSlack:17
ChromeLambdaLayerQualifiedArn: arn:aws:lambda:ap-northeast-1:*:layer:chrome:9
ServerlessDeploymentBucketName: incoming-webhook-dev-serverlessdeploymentbucket-*

Serverless: Removing old service artifacts from S3...
Serverless: Run the "serverless" command to setup monitoring, troubleshooting and testing.

参考

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

[Electron] IPC には新しい ipcRenderer.invoke() メソッドを使ったほうが便利 (v7+)

TL;DR;

Electron v7 から、ipcRenderer.invoke()ipcMain.handle() が新たに追加されました。これは、従来まで利用されてきた ipcRenderer.send()ipcRenderer.sendSync() の上位互換のようなものです。今後は積極的にこちらを使ったほうがよさそう。

従来の Renderer <-> Main プロセス間通信 (IPC)

同期: ipcRenderer.sendSync()

文字通り、同期 (Sync) 的にプロセス間通信を行います。
この際に重要なのは、sendSync によって Main プロセスが呼ばれるとその間は Renderer プロセス上の処理は完全にブロックされます。Main プロセスからの応答があるまでは、renderer プロセス側の操作画面はいわゆるフリーズしたような状態になります。描画処理も止まるので、ローディング画面のような CSS アニメーションも容赦なく固まります。

ドキュメントにも下記のように、どうしても使わざるを得ない状況下においてのみ最終手段として使う、そうでない場合は利用を避けることが推奨されています。

⚠️ WARNING: Sending a synchronous message will block the whole renderer process until the reply is received, so use this method only as a last resort. It's much better to use the asynchronous version, invoke().

renderer
// renderer から Main プロセスを呼び出す
const data = ipcRenderer.sendSync('sync-test', 'ping')
console.log(data)
main
ipcMain.on('sync-test', (event, message) => {
  // message には 呼び出し元からのデータ ('ping') が入っている
  console.log(message)
  event.returnValue = 'pong'
  return
})
console
> ping
> pong

Main プロセス側での処理が終わってから renderer 側で console.log が走るため、結果は上記のようになります。メリットとして、event.returnValue を通じて呼び出し元にデータを返すことができます。

非同期: ipcRenderer.send()

非同期実行のため、Main プロセスの処理中であっても renderer 側ではフリーズすることなく操作が可能です。

ただし Main プロセス側の処理がいつ終わったのか renderer 側では分からないので、これをトリガーに何らかの処理を行いたい場合、renderer 側で ipcRenderer.on() を定義しておくことで、Main -> rendere 方向の戻りの通信が可能になります。

renderer
// main からの呼び出しを待ち受ける
ipcRenderer.on('async-test-complete', (event, message) => {
    console.log(message)
)}

// renderer から Main プロセスを呼び出す
ipcRenderer.send('async-test', 'ping')
console.log('started')


main
ipcMain.on('async-test', (event, message) => {
  // message には 呼び出し元からのデータ ('ping') が入っている
  console.log(message)
  // main から renderer プロセスを呼び出す
  event.sender.send('async-test-complete', 'pong')
  return
})
console
> started
> ping
> pong

新しい Renderer <-> Main プロセス間通信 (IPC)

非同期: ipcRenderer.invoke()

ようやく本題です。Electron version 7+ から利用可能になりました。
こちらも非同期ですが、Main プロセスからは Promise が返ってきます。ですので、await を使って Main プロセスからのデータの受取を下記のようにシンプルに書くことができます。

renderer
// renderer から Main プロセスを呼び出す
const = data = await ipcRenderer.invoke('invoke-test', 'ping')
console.log(data)
main
ipcMain.handle('invoke-test', (event, message) => {
  // message には 呼び出し元からのデータ ('ping') が入っている
  console.log(message)
  // renderer プロセスにデータを返す
  return 'pong'
})
console
> ping
> pong

Main 側では、ipcMain.handle() とメソッド名が変わっています。

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

Oculus Quest で日本語を打つ

VR 空間でも日本語入力

rdgqs-1spna.gif
文字小さくてごめんなさい?

製作物

日本語メモ  (Glitch プロジェクト)
Oculus Quest なら Alt + Space で半角 ⇔ 日本語入力の切り替え。
URL を開くと ID が URL に付与されますので、再度編集したい場合は ID が付けられた URL をブックマークしてください。
テキストが変更がされると自動でサーバーに保存されます。

解説

Oculus Quest + Firefox Reality + Glitch + A-Frame で VR 内 VR 開発 を推進している gaegae です。

前回の記事でデメリットに上げませんでしたが、
2020/01/10現在、Oculus Quest では Bluetooth キーボードから日本語入力できません。
Firefox Reality ではソフトキーボードで日本語入力できますが、 Bluetooth は非対応です。
デフォルトブラウザはソフトキーボードも対応していません。

コーディング時に必須ではないので特に問題ないだろうと思っておりましたが、
作業しているときにひらめきを即座にメモできないのがきつく感じてきました?

英語で書くのはしんどい、メモのためにヘッドセット外したら本末転倒。
Firefox Reality のイシューに日本語入力をお願いしていますが、無理難題を Mozilla さんに押し付けるのも失礼。

なのでプログラマなのだから自分でどうにかしたいと思います。
VR 開発の記事をどんどん上げていきたいところですが、その前に足固めです。

方針

Oculus Quest の IME をどうにかするのは正直無理。 やる前からあきらめてました。
最低限、日本語のメモが取れればよかったので、
Javascript で日本語入力 を実装した Web ページを作成することにしました。

詳細

フロントエンドとサーバーサイド、2つの Glitch プロジェクトを作りました。

フロントエンド:https://glitch.com/~oculusquest-jpn-ime
テキストエリアが一つだけの Web ページです。
日本語入力には IgoIME を使用させていただいております。
変更があればサーバーに送信し、再度画面が表示されたときに読み込みます。

サーバーサイド:https://glitch.com/~dot-spruce
認証不要で使えるシンプルな Key Value Store です。
node.js 上でキーをファイル名、値を内容にしてファイルとして保存しています。
他のプロジェクトでも使い回せるように分けました。

おわりに

これでまたヘッドセット着脱の手間が減り、VRへの没入感がさらに上がりました。

今回は Oculus Quest よりも Glitch の話がメインでした。
やはりボタン一発で瞬時に Linux・node.js・IDEと Web 開発に必要なもの一通りを提供してくれるのは素晴らしいですね。
Google や AWS 、Github ページなどを利用するよりも気軽に開発・配信ができると思われますので
Web コンテンツ作りたい方、ぜひどうぞ!

bxeaf-a7wmf.gif
VR開発もやってます~。楽しー!

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

Oculus Quest + Bluetooth キーボードで日本語を打つ

VR 空間でも日本語入力

rdgqs-1spna.gif
文字小さくてごめんなさい?

製作物

日本語メモ  (Glitch プロジェクト)
Oculus Quest なら Alt + Space で半角 ⇔ 日本語入力の切り替え。
URL を開くと ID が URL に付与されますので、再度編集したい場合は ID が付けられた URL をブックマークしてください。
テキストが変更がされると自動でサーバーに保存されます。

解説

Oculus Quest + Firefox Reality + Glitch + A-Frame で VR 内 VR 開発 を推進している gaegae です。

前回の記事でデメリットに上げませんでしたが、
2020/01/10現在、Oculus Quest では Bluetooth キーボードから日本語入力できません。
Firefox Reality ではソフトキーボードで日本語入力できますが、 Bluetooth は非対応です。
デフォルトブラウザはソフトキーボードも対応していません。

コーディング時に必須ではないので特に問題ないだろうと思っておりましたが、
作業しているときにひらめきを即座にメモできないのがきつく感じてきました?

英語で書くのはしんどい、メモのためにヘッドセット外したら本末転倒。
Firefox Reality のイシューに日本語入力をお願いしていますが、無理難題を Mozilla さんに押し付けるのも失礼。

なのでプログラマなのだから自分でどうにかしたいと思います。
VR 開発の記事をどんどん上げていきたいところですが、その前に足固めです。

方針

Oculus Quest の IME をどうにかするのは正直無理。 やる前からあきらめてました。
最低限、日本語のメモが取れればよかったので、
Javascript で日本語入力 を実装した Web ページを作成することにしました。

詳細

フロントエンドとサーバーサイド、2つの Glitch プロジェクトを作りました。

フロントエンド:https://glitch.com/~oculusquest-jpn-ime
テキストエリアが一つだけの Web ページです。
日本語入力には IgoIME を使用させていただいております。
変更があればサーバーに送信し、再度画面が表示されたときに読み込みます。

サーバーサイド:https://glitch.com/~dot-spruce
認証不要で使えるシンプルな Key Value Store です。
node.js 上でキーをファイル名、値を内容にしてファイルとして保存しています。
他のプロジェクトでも使い回せるように分けました。

おわりに

これでまたヘッドセット着脱の手間が減り、VRへの没入感がさらに上がりました。

今回は Oculus Quest よりも Glitch の話がメインでした。
やはりボタン一発で瞬時に Linux・node.js・IDEと Web 開発に必要なもの一通りを提供してくれるのは素晴らしいですね。
Google や AWS 、Github ページなどを利用するよりも気軽に開発・配信ができると思われますので
Web コンテンツ作りたい方、ぜひどうぞ!

bxeaf-a7wmf.gif
VR開発もやってます~。楽しー!

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

開発環境の1つであるVSCodeの開発環境についてチラ見してみる

リーマンサットについて

趣味で宇宙開発を行う団体「リーマンサット・プロジェクト」がお送りする新春アドベントカレンダーです。インデックスはこちら

リーマンサット・プロジェクトは「普通の人が集まって宇宙開発しよう」を合言葉に活動をしている民間団体です。
他では経験できない「宇宙開発プロジェクト」に誰もが携わることができます。
興味を持たれた方は https://www.rymansat.com/join からお気軽にどうぞ。

概要

機械学習周りやWEBサービスを担当してますAkira Sugawaraです。

開発環境、、、エンジニアなら誰しも気になりますよね?
リーマンサットでは趣味開発なので、特に大きな制限を設けずに自由に開発しています。
開発エディタはVSCodeやIntelliJ、ソースコード・タスク管理はBacklog、といった感じです。

VSCodeの開発環境

ふと、VSCodeってどう作られてるんだろうか、どんな開発環境なんだろうか、と考えてしまいました。
思いを馳せると夜も眠れなくなってしまいます。

開発環境の一部であるエディタを開発している開発環境は、さぞこだわりがあるんだろう…。

ということで、本記事ではVSCodeの開発環境を調べてみます。

VSCodeのツールチェーン

2019/12/30時点でのmasterブランチをcloneして確認しています。。

type details
Language typescript,javascript
Application Framework Electron
Linter ESLint, TSLint
Formatter prettier
TaskRunner NPM, gulp
TestFramework mocha
CI/CD Azure Pipeline

CI/CDに関しては、./build/azure-pipelinesのフォルダの下にOS毎のビルド定義ファイルが格納されています。
さすがにMicrosoftのOSSということなので、Azureになってるんですね。

使用してるVSCodeExtension

もちろんVSCodeの開発もVSCodeですよね!
ワークスペースのRecommendリストは下記です。

  1. "ms-vscode.vscode-typescript-tslint-plugin"

    eslintがtypescriptサポートを強化するという発表もありましたが、フロントエンドによくあるESLint/TSLint併用構成のようです

  2. "dbaeumer.vscode-eslint"

    言わずもしれたESLintのvscodeプラグインです。

  3. "EditorConfig.EditorConfig"

    恥ずかしながら使用経験がありませんでした。
    複数人で作業するときのEditorのConfigurationToolで、言語非依存・エディター非依存で規約の準拠が可能になります。
    Prettierと役割重なってるんじゃない?と思いましたが、この記事が違いがわかりやすかったです。

  4. "msjsdiag.debugger-for-chrome"

    Chromeでjavascriptをデバックするための連携ツールです。

まとめ

さらーっと、VSCodeの開発環境を見ていきました。
何かしらのプロジェクトで新しく開発を始める場合、著名なOSSの環境を見てみるのは参考になるかもしれませんね。

次の記事は、@KingBritainの「航空機の流体力学(2次元翼の流体解析)」になります。

参考

GitHub
How to build

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