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

npmとyarnとは?

はじめに Rails開発を進めていく中で、使い分けがわからなかったためまとめました Node.js サーバーサイドJavaScriptとも呼ばれている。 JavaScriptはブラウザ上でしか動けなかったが、Rubyなどのようにパソコン上で動作可能にしてくれるJavaScript実行環境である。 npmとは Node Package Maneger Node.jsで書かれている様々なパッケージを管理している 例: Ruby(gem) Mac(Homebrew) npm(Node.js) Node.jsで使いたいライブラリがあれば、npmでインストールできる Vue.jsのインストール $ npm install vue 使い方 ①ディレクトリをnpmの管理下に置く $ npm init これでpackage.json(パッケージに関する情報を格納するファイル)が作成される ②パッケージをインストールする Vue.jsのインストール $ npm install vue これでパッケージがインストールされる yarn こちらもnpmと同じくNode.jsのパッケージマネージャー npmと互換性があり、package.jsonが併用できる 機能面での差はほとんどないが、npmの欠点を補うように開発されている 使い方 ①yarnのインストール $ brew install yarn ②package.jsonが存在しない場合、 $ yarn init ③パッケージのインストール $ yarn add (パッケージ) 付随するファイル node_modules インストールしたパッケージのインストール先 package.json 依存関係を記載したJSONファイル ここに記述しておけば、インストールの際にパッケージがインストールされる そのため、githubから引っ張ってきて、自分の環境でnpm installをすることで、 同様のパッケージが使用できる package-lock.json npm install時に作成される そこで実際にインストールされたパッケージ情報が記載されている 参考文献 Node.jsとはなにか?なぜみんな使っているのか? https://qiita.com/non_cal/items/a8fee0b7ad96e67713eb yarnとは https://qiita.com/akitxxx/items/c97ff951ca31298f3f24 Yarn:Facebook発のパッケージマネジャーはnpmに代わるスタンダードになるか https://www.webprofessional.jp/yarn-vs-npm/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

npm package をアップデートしたい時の便利なコマンド

npm package をアップデートしたい時のコマンド npm packageをアップデートしたい時によく使用するコマンドについてまとめます。 今回packageを見ていくのは vue-hackernews-2.0 をです。 $ git clonse https://github.com/vuejs/vue-hackernews-2.0.git $ cd vue-hackernews-2.0 npm outdated outdatedコマンドは、レジストリをチェックして、インストールされている(または特定の)パッケージが現在古くなっているかどうかを確認する。 デフォルトでは、ルートプロジェクトの直接の依存関係と構成されたワークスペースの直接の依存関係のみが表示する。 --allすべての古いメタ依存関係も検索するために使用する。 実行 $ npm outdated Package Current Wanted Latest Location autoprefixer 7.2.6 7.2.6 10.2.6 vue-hackernews-2.0 babel-loader 7.1.5 7.1.5 8.2.2 vue-hackernews-2.0 chokidar 1.7.0 1.7.0 3.5.2 vue-hackernews-2.0 cross-env 5.2.1 5.2.1 7.0.3 vue-hackernews-2.0 css-loader 0.28.11 0.28.11 5.2.6 vue-hackernews-2.0 file-loader 1.1.11 1.1.11 6.2.0 vue-hackernews-2.0 firebase 4.6.2 4.6.2 8.6.8 vue-hackernews-2.0 lru-cache 4.1.5 4.1.5 6.0.0 vue-hackernews-2.0 rimraf 2.7.1 2.7.1 3.0.2 vue-hackernews-2.0 route-cache 0.4.3 0.4.3 0.4.5 vue-hackernews-2.0 stylus-loader 3.0.2 3.0.2 6.1.0 vue-hackernews-2.0 sw-precache-webpack-plugin 0.11.5 0.11.5 1.0.0 vue-hackernews-2.0 url-loader 0.6.2 0.6.2 4.1.1 vue-hackernews-2.0 vue 2.6.12 2.6.14 2.6.14 vue-hackernews-2.0 vue-server-renderer 2.6.12 2.6.14 2.6.14 vue-hackernews-2.0 vue-template-compiler 2.6.12 2.6.14 2.6.14 vue-hackernews-2.0 webpack 3.12.0 3.12.0 5.39.1 vue-hackernews-2.0 webpack-dev-middleware 1.12.2 1.12.2 5.0.0 vue-hackernews-2.0 webpack-merge 4.2.2 4.2.2 5.8.0 vue-hackernews-2.0 webpack-node-externals 1.7.2 1.7.2 3.0.0 vue-hackernews-2.0p current: 現在インストールされているバージョン wanted: 存在するバージョンのうち、 package.json に記載された semver 条件を満たす最新のバージョン。 Latest: レジストリで最新としてタグ付けされているパッケージのバージョン Location: 物理ツリーのどこにパッケージが配置されているか ※ semverとは? Semantic Versioning の略 3.14.1 一番左側の数字をメジャーバージョン、真ん中の数字をマイナーバージョン、一番右側の数字をパッチバージョン パッチバージョン:後方互換性のあるバグ修正やドキュメントの変更などをした場合、パッチバージョンの値をひとつ大きくする。 マイナーバージョン:後方互換性のある機能追加とドキュメントの追記をした場合、マイナーバージョンの値をひとつ大きくする。 メジャーバージョン:後方互換性のない変更がある場合、メジャーバージョンの値をひとつ大きくする。 npm update このコマンドはtag、semverを尊重して、リストされているすべてのパッケージを最新バージョン(構成で指定)に更新する。 また、不足しているパッケージもインストールする。 マイナーバージョンまで最新化される。 実行 $ npm update npm WARN ajv-keywords@3.5.2 requires a peer of ajv@^6.9.1 but none is installed. You must install peer dependencies yourself. + vue-template-compiler@2.6.14 + vue@2.6.14 + vue-server-renderer@2.6.14 updated 3 packages and audited 1001 packages in 4.595s 12 packages are looking for funding run `npm fund` for details found 15 vulnerabilities (4 low, 8 moderate, 3 high) run `npm audit fix` to fix them, or `npm audit` for details vue-template-compiler@2.6.14 vue@2.6.14 vue-server-renderer@2.6.14 の3つのパッケージがlatest(マイナーバージョンの最新バージョン)に更新されました。 npm audit auditコマンドは、プロジェクトで構成されている依存関係の説明をデフォルトのレジストリに送信し、既知の脆弱性のレポートを要求する。 脆弱性が見つかった場合は、影響と適切な修正が必要な箇所が表示される。 low(低レベル), moderate(中レベル), high(高レベル), critical(致命的) の 4種類に分かれます。(infoというのもあります。) https://docs.npmjs.com/cli/v7/commands/npm-audit#audit-level 基本的に全てに対応したいですが、BreakingChangeなどもありなかなか対応できないものもあります。しかしcriticalとhighについては絶対対応すべきです。 実行 $ npm audit ..... found 15 vulnerabilities (4 low, 8 moderate, 3 high) in 1001 scanned packages run `npm audit fix` to fix 2 of them. 12 vulnerabilities require semver-major dependency updates. 1 vulnerability requires manual review. See the full report for details.. (訳) スキャンした1001個のパッケージから15個の脆弱性(低4個、中8個、高3個)が見つかりました。 npm audit fix` を実行して、そのうちの2つを修正しました。 12個の脆弱性は準メジャーな依存関係の更新が必要です。 1つの脆弱性は手動でのレビューが必要です。詳細は報告書の全文をご覧ください。。 ┌───────────────┬──────────────────────────────────────────────────────────────┐ │ High │ Regular Expression Denial of Service │ ├───────────────┼──────────────────────────────────────────────────────────────┤ │ Package │ normalize-url │ ├───────────────┼──────────────────────────────────────────────────────────────┤ │ Dependency of │ css-loader [dev] │ ├───────────────┼──────────────────────────────────────────────────────────────┤ │ Path │ css-loader > cssnano > postcss-normalize-url > normalize-url │ ├───────────────┼──────────────────────────────────────────────────────────────┤ │ More info │ https://npmjs.com/advisories/1755 │ └───────────────┴──────────────────────────────────────────────────────────────┘ More info の URIにアクセスすると脆弱性内容の詳細を見ることができます。 今回は、 https://www.npmjs.com/advisories/1755 Overview The xmlhttprequest-ssl package before 1.6.1 for Node.js disables SSL certificate validation by default, because rejectUnauthorized (when the property exists but is undefined) is considered to be false within the https.request function of Node.js. In other words, no certificate is ever rejected. Remediation Upgrade to version 1.6.1 or later (訳) 概要 Node.jsの1.6.1以前のxmlhttprequest-sslパッケージは、デフォルトでSSL証明書の検証を無効にしています。これは、Node.jsのhttps.request関数内でrejectUnauthorized(プロパティが存在するが未定義の場合)がfalseとみなされるためです。つまり、証明書が拒否されることはない。 対応策 バージョン1.6.1以降にアップグレードする。 正規表現を使ったDoS攻撃(ReDoS)を食らう危険があるらしい。。。 自動で修正する方法 npm audit コマンドには脆弱性のある箇所を自動修正してくれるサブコマンドfixがあります $ npm audit fix found 13 vulnerabilities (3 low, 7 moderate, 3 high) in 1001 scanned packages 12 vulnerabilities require semver-major dependency updates. 1 vulnerability requires manual review. See the full report for details. npm ls lsコマンドは、インストールされているパッケージのすべてのバージョンと依存関係をツリー構造で標準出力に出力する。 実行 今回は npm audit コマンドで脆弱性 high の normalize-url について見てみます。 npm ls <package名> $ npm ls normalize-url vue-hackernews-2.0@ /Users/taimikam/workspace/vue-hackernews-2.0 └─┬ css-loader@0.28.11 └─┬ cssnano@3.10.0 └─┬ postcss-normalize-url@3.0.8 └── normalize-url@1.9.1 npm dedupe ローカルパッケージツリーを検索し、依存関係をツリーのさらに上に移動することで全体的な構造を単純化しようとする。依存関係は、複数の依存パッケージでより効果的に共有できる。 以下のような依存関係ツリーがあるとする。 a +-- b <-- depends on c@1.0.x | `-- c@1.0.3 `-- d <-- depends on c@~1.0.9 `-- c@1.0.10 npm dedupe は以下のように変換する。 a +-- b +-- d `-- c@1.0.10 その他便利packageについて npm-check-updates https://github.com/raineorshine/npm-check-updates npm-check-updatesは、指定されたバージョンを無視して、package.jsonの依存関係を最新バージョンにアップグレードします。 実行 ❯ ncu Checking /Users/hoge/workspace/vue-hackernews-2.0/package.json [====================] 35/35 100% compression ^1.7.1 → ^1.7.4 cross-env ^5.1.1 → ^7.0.3 es6-promise ^4.1.1 → ^4.2.8 express ^4.16.2 → ^4.17.1 firebase 4.6.2 → 8.6.8 lru-cache ^4.1.1 → ^6.0.0 serve-favicon ^2.4.5 → ^2.5.0 vue-router ^3.0.1 → ^3.5.1 vuex ^3.0.1 → ^3.6.2 autoprefixer ^7.1.6 → ^10.2.6 babel-core ^6.26.0 → ^6.26.3 babel-loader ^7.1.2 → ^8.2.2 babel-preset-env ^1.6.1 → ^1.7.0 chokidar ^1.7.0 → ^3.5.2 css-loader ^0.28.7 → ^5.2.6 file-loader ^1.1.5 → ^6.2.0 friendly-errors-webpack-plugin ^1.6.1 → ^1.7.0 rimraf ^2.6.2 → ^3.0.2 stylus ^0.54.5 → ^0.54.8 stylus-loader ^3.0.1 → ^6.1.0 sw-precache-webpack-plugin ^0.11.4 → ^1.0.0 url-loader ^0.6.2 → ^4.1.1 vue-loader ^15.3.0 → ^15.9.7 webpack ^3.8.1 → ^5.39.1 webpack-dev-middleware ^1.12.0 → ^5.0.0 webpack-hot-middleware ^2.20.0 → ^2.25.0 webpack-merge ^4.2.1 → ^5.8.0 webpack-node-externals ^1.7.2 → ^3.0.0 現在のバージョン → 最新バージョン を表示してくれます。 ncu -u を実行すると package.jsonを強制的に最新packageを使用するように書き換えてくれます。 最後に 今回紹介した以外にも npm package を更新するのに便利なコマンドがあると思います。 CIなどと組み合わせて自動更新スクリプトを組むのも良いかもしれませんね。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

gyp: No Xcode or CLT version detected!

gyp: No Xcode or CLT version detected! gyp: No Xcode or CLT version detected! gyp ERR! configure error gyp ERR! stack Error: `gyp` failed with exit code: 1 gyp ERR! stack at ChildProcess.onCpExit (/Users/manowaraiuma/.nodebrew/node/v10.23.0/lib/node_modules/npm/node_modules/node-gyp/lib/configure.js:351:16) gyp ERR! stack at ChildProcess.emit (events.js:198:13) gyp ERR! stack at Process.ChildProcess._handle.onexit (internal/child_process.js:248:12) gyp ERR! System Darwin 19.6.0 gyp ERR! command "/Users/manowaraiuma/.nodebrew/node/v10.23.0/bin/node" "/Users/manowaraiuma/.nodebrew/node/v10.23.0/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild" gyp ERR! cwd /Users/manowaraiuma/Desktop/n010/node_modules/watchpack-chokidar2/node_modules/fsevents gyp ERR! node -v v10.23.0 gyp ERR! node-gyp -v v5.1.0 gyp ERR! not ok Xcodeの再インストール $ sudo rm -rf $(xcode-select -print-path) $ sudo rm -rf /Library/Developer/CommandLineTools $ xcode-select --install 上記のコマンドで解決しました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

windows で npm の shell 設定を変更したら別窓に開くようなってしまったのを直す

たちまちやり方がわからなかったのでnpmの設定ファイルのリセットで直しました。 npmの設定ファイルを修正すればリセットする必要はないと思われますが、方法がわかりません。 ユーザーのデフォルトをリセットするには これをコマンドライン(またはWindowsではgit bash)で実行します。 echo "" > $(npm config get userconfig) npm config edit グローバルデフォルトをリセットするには sh echo "" > $(npm config get globalconfig) npm config --global edit sudoが必要な場合は、代わりにこれを実行してください。 sudo sh -c 'echo "" > $(npm config get globalconfig)' 引用元 node.js - How to restore/reset npm configuration to default values? - Stack Overflow https://stackoverflow.com/questions/20934343/how-to-restore-reset-npm-configuration-to-default-values
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

nodejsを使ってwebページからデバイスを制御する

何ができるようになるか webページを使ったインタラクティブなシステムの設計ができるようになります。 例えば、webページのボタンを世界の誰かがクリックするとLEDが点灯したり。 この記事は、実装重視で行くので細かいところは、調べてください。 システム構成 nodejsでwebサーバーを立てて、webページをホスティングします。webページの閲覧者はサーバーに対してクライアントと呼ばれます。 nodejsのsocket.ioというライブラリを使うことによってチャットのような機能を実現することができます。webページのボタンをクリックすることで何かデバイスを制御するということは、チャットでもし〜という文字が打たれたら〜を実行するということと同じです。 手順 実際に全世界からサーバーに接続してもらうためには、サーバーを借りなければいけません。しかし、今の時代これくらいの機能であれば完全無料でできます。 この記事では自分のパソコン(ローカル)で一通り、流れを押さえた上で、glitch.comを使用して、webページを公開し、全世界からアクセスを受け付けたいと思います。 とは言っても、ローカルでチャットアプリを実現する方法は探せばたくさん出てくるので、この記事では、その後の全世界に公開するところからに注力します。 具体的な手順を示します。 1. ローカルサーバーを立てる 1.1 nodejsのインストール 1.2 サーバーコードを書く 1.3 クライアントコードを書く 2. glitchに移植する 3. nodejsアプリケーションを作成する 4. デバイスを制御する glitchへの移植まで飛ばしたい人はこちら 環境 Mac node.jsのインストール nodejsをMacにインストールする方法は色々とありますが、nodejsのバージョン管理ができるnvmを使用してインストールすることをお勧めします。 この辺りは、記事がたくさん出ているので参考にしながら進めてください。 公式ドキュメント nvm + Node.js + npmのインストール nvm(Node Version Manager)を使ってNode.jsをインストールする手順 サーバーを立ててwebページでチャットを実現する こちらも nodejs socket.io チャット とかで調べるとたくさん参考が出てくるので、調べてください。 こちらの記事をお勧めします。 Node.js + Express + Socket.ioで簡易チャットを作ってみる こちらの記事ではnodejsのsocket.ioとexpressというライブラリを用いて、チャットアプリを制作しています。 glitchに移植する glitch.comを開いて、sign up しましょう。 色々バグることがあるのでGoogle Chromeがお勧めです。 glitchは、オンライン上でコードの編集から公開までできるとても便利なサービスです。 glitchでは人が作ったコードremixという形で、流用することができます。 こちらからremixできるので自分でやるのがめんどくさい人は、リンクに飛んで、右上のremix to edit を選択してください サインアップしたら、右上のNew Projectを押してglitch-hello-nodeを選択します。 開くと、サンプルファイルがあると思いますが、public/, src/, server.jsは不要なのでそれらのファイル名の右側にあるメニューからdeleteを選択して消してしまいましょう。 続いて、前の項で作ったapp.js, index.htmlをアップロードします。 New FileからUpload Fileを選択します。 そして、足りないモジュールを追加していきます。 glitch画面左下のtoolsからTerminalを選択します。 (Safariだとターミナルに書き込めないことがあります) $ npm install express --save $ npm install socket.io --save 最後にpackage.jsonの5〜7行目mainとなるコードをapp.jsに書き換えます。 "main": "app.js", "scripts": { "start": "node app.js" }, これで、サーバーが正常に機能するはずです。 つまり、全世界からこのwebサイトにアクセスすることができます。 左上のShowからIn a new windowを選択して、webページを開きましょう。 このリンクは全世界に公開されてるのでスマホなどからアクセスすることもできます。 デバイスでメッセージを受け取る さて、本題です。どうしたらデバイスでこのメッセージを受け取れるでしょう。 実はnodejsではsocket.ioを用いた通信をwebページ以外からも受け取ることができます。 nodejsが実行できるデバイス(macやraspberry pi)で受信用のプログラムを作成します。 まず、作業用のフォルダを作成し、必要なモジュールをnpmでインストールします。 $ mkdir receiver $ cd receiver $ npm install socket.io-client --save $ touch client.js 作成したclient.jsの中身を書いていきます。 client.js const io = require('socket.io-client'); var socket = io.connect('glitchなどのURL');//接続先のサーバを指定 socket.on('message' ,function (data) { console.log(data); }); 接続先のサーバには、先ほどglitchで公開したwebページのurlを指定します。 これを実行します。 $ node client.js そして、先ほど作ったglitch上のwebサイトから何か送ってみましょう。 表示されたでしょうか。 アプリケーションとの連携 さて、ここからこの値を使ってデバイスを実際に制御します。 ここでは例としてこのデータをOSCを用いて他のアプリケーションに流す方法、raspberry piなどでGPIOを用いてLチカする方法を紹介します。 OSCを用いて他のアプリケーションに流す OSCを流すためのモジュールをインストールします。 OSC知らない人はとても便利なので一度ググってね。 $ npm install node-osc --save OSCを送るためにclient.jsを改変します。 client.js const { Client, Server } = require('node-osc'); const io = require('socket.io-client'); const client = new Client('127.0.0.1', 3333); var socket = io.connect('glitchなどのURL');//接続先のサーバを指定 socket.on('message' ,function (data) { console.log(data) client.send('/message', String(data)); //型に注意 }); この例では、自分のパソコンの3333ポートにwebページで入力された内容を送っています。 一点注意するべきは、送るdataの型に注意することです。JavaScriptは基本的に型を宣言することがないので、気にならないですが、例えば、Touch DesingerのOSC In Chopにデータを送りたいときは、String()を受け取ることができないのでInt()かFloat()にしてあげる必要があります。 さて、どうでしょう。他のアプリケーションで受け取れたでしょうか。 GPIOを制御してLチカ こちらはraspberry piなどのGPIOを制御する例です。webページで0が送られてきたらLEDをオフ、1がきたらLEDをオンするようにしています。 $ npm install onoff --save GPIOを制御するためにclient.jsを改変します。 client.js const io = require('socket.io-client'); const Gpio = require('onoff').Gpio var led = new Gpio(8, 'out') var socket = io.connect('glitchなどのURL');//接続先のサーバを指定 socket.on('message' ,function (data) { console.log(data) if(data == '0'){ led.writeSync(0) }else if(data == '1'){ led.writeSync(1) } }); まとめ Webサイトから何かを制御するって楽しいですよね! ぜひ自分が作ったウェブサイトのリンクをTwitterなどで公開していろんな人にLチカしてもらいましょう!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AlexaのAPLでゲームブックを作る

Alexaには、APL(Alexa Presentation Language)という機能があり、Echo ShowやFireタブレットによるShowモードで、画面付きのスマートスピーカのための画面表示をするための仕組みがあります。 手元に、Echo Showはないですが、ブラウザ上で試せるシミュレータがあるので、試しながらゲームブック実行環境を作ってみようと思います。 もろもろのソースコードは以下のGitHubに置いてあります。 poruruba/AlexaAplGamebook 参考ページ ・Alexa Presentation Language(APL)  https://developer.amazon.com/ja-JP/docs/alexa/alexa-presentation-language/understand-apl.html スキルを新規作成 以下のページからまずはAlexaスキルを作成します。 alexa developer console https://developer.amazon.com/alexa/console/ask 「スキルを作成」ボタンを押下します。 スキル名は適当にたとえば「テストゲームブック」とし、カスタムとalexa-hosted(Node.js)を選択します。 最後に、右上の「スキルを作成」ボタンを押下します。 スクラッチで作成を選択して、「テンプレートで続ける」ボタンを押下します。 作成が完了すると、以下のように表示されます。 インテントを追加 以下のインテントを作ります。 ・GameStartIntet   ゲームブックを最初から始めるときの言葉です。   サンプル発話は、「始める」です。 ・ChoiceNextIntent   ゲームブックのシーンを進めるときの言葉です。   サンプル発話は、「次」と「次へ」です。 ・ChoiceSelectIntent   ゲームブックでの選択肢を選択するときの言葉です。   サンプル発話は、「{select}」と「{select}番」です。{select}は、インテントスロットに追加するもので、スロットタイプは、「AMAZON.NUMBER」にします。 HelloWorldIntentは使わないので削除します。 次に、左側のナビゲーションメニューから、インターフェースを選択します。 するとそこに、「Alexa Presentation Language」がありますのでそれをOnにして有効にします。 最後に、「インターフェースを保存」ボタンを押下します。 次に、同様に左側のナビゲーションメニューから、エンドポイントを選択します。 サービスのエンドポイントの種類として、HTTPSを選択します。Lambdaでもよいですが、まずはのちほど自分でLambda実行環境を立ち上げます。 デフォルトの地域のところに、これから立ち上げるLambda実行環境のURLを指定します。  https://【立ち上げるLambda実行環境のホスト名】/apl-gamebook  「開発用のエンドポイントには、信頼された証明機関が発行した証明書があります。」 立ち上げるホストのポート番号は443である必要があります。 最後に、「エンドポイントを保存」ボタンを押下します。 左側のナビゲーションメニューからカスタムを選択して最初の画面に戻ったら、3.モデルをビルド> をクリックします。ビルドが始まります。少し時間がかかりますが、完了すると、画面右下にポップアップで表示してくれます。 画面をデザインする 左側のナビゲーションメニューから、マルチモーダルをクリックします。 そして、「Visual」タブを選択し、「Create Visual Response」ボタンを押下します。 「空のドキュメント」ボタンを押下します。 以下のようなページが表示されます。 APLオーサリングツールというそうです。 このページで、表示された状態を見ながら画面を作ることができます。 ですが、すでに作ってありますので、改めての作成不要です。 Lambdaサーバを立ち上げる 以下のGitHubサイトから丸ごとZIPダウンロードします。 https://github.com/poruruba/AlexaAplGamebook > unzip AlexaAplGamebook-master.zip > cd AplGamebook-master > mkdir cert > npm install > touch .env HTTPSで立ち上げる必要があるため、certフォルダに、SSL証明書を置く必要があります。 ポート番号は.envファイルに以下を記載します。 SPORT=443 あとは、以下で立ち上がります。これで、インテントが飛んでくるので、ソースコードにブレイクを入れたりして手元でデバッグできるようになります。 > node app.js ゲームブックのシナリオファイルの作成 ゲームブックのシナリオファイルを作成します。APLとは関係ないです。 以下にサンプルを記載しておきました。お好みで編集してください。 /api/controllers/apl-gamebook/scenario.json 下記にある通り、ビデオファイルや画像ファイルは、いずれかからとってきて、以下のフォルダに格納しておきます。 /public/media/ { "scenes": [ { "id": "id0", "type": "normal", "title": "昔話", "backgroundImage": "https://【立ち上げたサーバのホスト名】/media/bg_yozora_night_sky.jpg", "sentences": [ "昔々あるところに、おじいさんとおばあさんがいました。" ], "players": [ { "image_src": "https:// 【立ち上げたサーバのホスト名】/media/ojiisan.png", "height": 80, "position": 4 }, { "image_src": "https:// 【立ち上げたサーバのホスト名】/media/obaasan.png", "height": 60, "position": 8 } ], "choices": { "text": "選択してください", "choices": [ { "text": "おじいさんと話す", "choice_id": "id1" }, { "text": "おばあさんと話す", "choice_id": "id2" } ] } }, { "id": "id1", "type": "normal", "headerTitle": "昔話", "backgroundImage": "https:// 【立ち上げたサーバのホスト名】/media/bg_yozora_night_sky.jpg", "sentences": [ "こんにちは。", "わしはおじいさんです。" ], "players": [ { "image_src": "https:// 【立ち上げたサーバのホスト名】 /media/ojiisan.png", "height": 80, "position": 6 } ], "choices": { "text": "選択してください", "choices": [ { "text": "戻る", "choice_id": "id0" } ] } }, { "id": "id2", "type": "normal", "headerTitle": "昔話", "backgroundImage": "https:// 【立ち上げたサーバのホスト名】/media/bg_yozora_night_sky.jpg", "sentences": [ "こんにちは", "わたしはおばあさんです。" ], "players": [ { "image_src": "https:// 【立ち上げたサーバのホスト名】/media/obaasan.png", "height": 60, "position": 6 } ], "choices": { "text": "選択してください", "choices": [ { "text": "戻る", "choice_id": "id0" } ] } }, { "id": "0", "type": "video", "video_src": "https:// 【立ち上げたサーバのホスト名】/media/Pexels Videos 4703.mp4", "title": "昔話", "choice_id": "id0", "sentences": [ "ビデオが流れます。" ] } ] } 上記で利用しているフォーマットは以下の通りです。 { "scenes": [ { "id": "【シーンのID(0がスタートシーン)】", "type": "【normalまたはvideo。以降はnormalの場合】", "title": "【タイトル】", "headerTitle": "【ヘッダーサブタイトル】", "backgroundImage": "【背景画像ファイルのURL】", "sentences": [ "【表示・発話したい文書】" ], "players": [ { "image_src": "【登場人物画像ファイルのURL】", "height": 【登場人物の高さ(%)】, "position": 【登場人物の位置(0~12、6が中央)】 } ], "choices": { "text": "【選択肢のページの文章】", "choices": [ { "text": "【選択肢の文章】", "choice_id": "【選択後のシーンID】" } ] } { "id": "【シーンのID(0がスタートシーン)】", "type": "【normalまたはvideo。以降はvideoの場合】", "video_src": "【ビデオファイルのURL】", "title": "【タイトル】", "choice_id": "【ビデオ表示後のシーンID】", "sentences": [ "【ビデオが表示できない端末用の文章】" ] } ] } ソースコード解説 /api/controllers/apl-gamebook/index.js 'use strict'; const HELPER_BASE = process.env.HELPER_BASE || '../../helpers/'; const AskUtils = require(HELPER_BASE + 'alexa-utils'); const Alexa = require('ask-sdk-core'); const app = new AskUtils(Alexa); const gamebookDocumentBase = require('./gamebookDocumentBase.json'); const gamebookVideoDocumentBase = require('./gamebookVideoDocumentBase.json'); const scenario = require('./scenario.json'); const styleResource = require('./styleResource.json'); const HELLO_WORLD_TOKEN = 'helloworldToken'; const CHOICE_INTENT = 'choice'; const PARAMETER_KEY = "gamebookStyles"; const SCENE_START = "0"; app.intent("ChoiceSelectIntent", async (handlerInput) => { console.log(handlerInput); var builder = handlerInput.responseBuilder; var slots = app.getSlots(handlerInput); var select = parseInt(slots.select.value); var attributes = app.getAttributes(handlerInput); var choice_id = findChoiceId(scenario, attributes.current_id, select - 1); attributes.current_id = choice_id; app.setAttributes(handlerInput, attributes); var scene = scenario.scenes.find(item => item.id == choice_id); appendSceneDocument(handlerInput, builder, scene); return builder.getResponse(); }); app.intent("ChoiceNextIntent", async (handlerInput) => { console.log(handlerInput); var builder = handlerInput.responseBuilder; var select = 1; var attributes = app.getAttributes(handlerInput); var choice_id = findChoiceId(scenario, attributes.current_id, select - 1); attributes.current_id = choice_id; app.setAttributes(handlerInput, attributes); var scene = scenario.scenes.find(item => item.id == choice_id); appendSceneDocument(handlerInput, builder, scene); return builder.getResponse(); }); app.intent('LaunchRequest', async (handlerInput) => { console.log(handlerInput); var builder = handlerInput.responseBuilder; builder.speak('始める、と言ってください。'); builder.reprompt('始める、と言ってください。'); return builder.getResponse(); }); app.intent('StopIntent', async (handlerInput) => { console.log(handlerInput); var builder = handlerInput.responseBuilder; builder.speak('さようなら'); builder.withShouldEndSession(true); return builder.getResponse(); }); app.intent('GameStartIntent', async (handlerInput) => { var builder = handlerInput.responseBuilder; var choice_id = SCENE_START; var attributes = app.getAttributes(handlerInput); attributes.current_id = choice_id; app.setAttributes(handlerInput, attributes); var scene = scenario.scenes.find(item => item.id == choice_id); appendSceneDocument(handlerInput, builder, scene); return builder.getResponse(); }); app.userEvent(undefined, async (handlerInput) => { var builder = handlerInput.responseBuilder; var request = app.getUserEventRequest(handlerInput); var choice_id = request.arguments[0].choice_id; var attributes = app.getAttributes(handlerInput); attributes.current_id = choice_id; app.setAttributes(handlerInput, attributes); var scene = scenario.scenes.find(item => item.id == choice_id); appendSceneDocument(handlerInput, builder, scene); return builder.getResponse(); }); app.userEvent(CHOICE_INTENT, async (handlerInput) => { var builder = handlerInput.responseBuilder; var request = app.getUserEventRequest(handlerInput); var choice_id = request.arguments[0].choice_id; var attributes = app.getAttributes(handlerInput); attributes.current_id = choice_id; app.setAttributes(handlerInput, attributes); var scene = scenario.scenes.find(item => item.id == choice_id); appendSceneDocument(handlerInput, builder, scene); return builder.getResponse(); }); exports.handler = app.lambda(); function appendSceneDocument(handlerInput, builder, scene) { if (Alexa.getSupportedInterfaces(handlerInput.requestEnvelope)['Alexa.Presentation.APL']) { var gamebookbase = makeDocument(scene, PARAMETER_KEY); builder.addDirective(app.buildRenderDocumentDirective(HELLO_WORLD_TOKEN, gamebookbase, styleResource)); if (scene.type != 'video') { var sentence = makeSentence(scene); builder.speak(sentence); builder.reprompt('次と言ってください'); } } else { var sentence = makeSentence(scene); builder.speak(sentence); builder.reprompt('番号を言ってください'); } } function findChoiceId(scenario, current_id, select) { var scene = scenario.scenes.find(item => item.id == current_id); if (!scene) return null; if( scene.type == 'video') return scene.choice_id; var choice = scene.choices.choices[select]; if (!choice) return null; return choice.choice_id; } function makeDocument(scene, param_key) { if (!scene) return null; if (scene.type == 'normal') { var gamebookbase = JSON.parse(JSON.stringify(gamebookDocumentBase)); setParameterKey(gamebookbase, param_key); setBackgroundImage(gamebookbase, scene.backgroundImage); setTitle(gamebookbase, scene.title); setHeaderTitle(gamebookbase, scene.headerTitle); if (scene.players && scene.players.length > 0) { for (var i = 0; i < scene.players.length; i++) pushPlayer(gamebookbase, scene.players[i].image_src, scene.players[i].height, scene.players[i].position); } if (scene.sentences && scene.sentences.length > 0) { appendSentencePage(gamebookbase); for (var i = 0; i < scene.sentences.length; i++) appendText(gamebookbase, scene.sentences[i]); } if (scene.choices) { appendChoicePage(gamebookbase, scene.choices.text); for (var i = 0; i < scene.choices.choices.length; i++) appendChoice(gamebookbase, CHOICE_INTENT, scene.choices.choices[i].text, { choice_id: scene.choices.choices[i].choice_id }); } console.log(JSON.stringify(gamebookbase)); return gamebookbase; } else if (scene.type == "video") { var gamebookbase = JSON.parse(JSON.stringify(gamebookVideoDocumentBase)); setParameterKey(gamebookbase, param_key); setVideoTitle(gamebookbase, scene.title); setVideo(gamebookbase, CHOICE_INTENT, scene.video_src, { choice_id: scene.choice_id }); console.log(JSON.stringify(gamebookbase)); return gamebookbase; } } function makeSentence(scene) { if (!scene) return null; if (scene.type == 'normal') { var sentence = ''; if (scene.sentences && scene.sentences.length > 0) { for (var i = 0; i < scene.sentences.length; i++) sentence += scene.sentences[i] + '。'; } if (scene.choices) { sentence += scene.choices.text + '。'; for (var i = 0; i < scene.choices.choices.length; i++) sentence += String(i + 1) + '、' + scene.choices.choices[i].text + '。'; } console.log(sentence); return sentence; } else if (scene.type == "video") { var sentence = ''; if (scene.sentences && scene.sentences.length > 0) { for (var i = 0; i < scene.sentences.length; i++) sentence += scene.sentences[i] + '。'; } return sentence; } } function getPlayerItems(document) { return document.mainTemplate.items[0].items[2].items; } function getContentItems(document) { return document.mainTemplate.items[0].items[3].items; } function getPagerItems(document) { var content = getContentItems(document); return content[1].item[0].items; } function getSentencePage(document) { var pager = getPagerItems(document); var page = pager[0]; if (page == null || page.type != 'Sequence') return null; else return page; } function getChoicePage(document) { var pager = getPagerItems(document); var page = pager[pager.length - 1]; if (page == null || page.type != 'Container') return null; else return page; } function setParameterKey(document, param_key){ document.mainTemplate.parameters.push(param_key); } function setBackgroundImage(document, image){ document.mainTemplate.items[0].items[0].backgroundImageSource = image; } function setHeaderTitle(document, title) { document.mainTemplate.items[0].items[1].text = title; } function setTitle(document, title) { var content = getContentItems(document); content[0].text = title; } function pushPlayer(document, image, height, position){ var pos = Math.floor((position - 6) * (50 / 6)); var obj = { "type": "Image", "height": height + "%", "width": "100%", "scale": "best-fit", "source": image, "position": "absolute", "align": "bottom", "left": pos + "%" }; var items = getPlayerItems(document); items.push(obj); } function appendText(document, text){ var page = getSentencePage(document); if( page == null ) return; var index = page.items.length; var obj = { "type": "Text", "id": "para_" + index, "text": text, "speech": text, "color": "${gamebookStyles.gb_font_color}", "paddingBottom": "${gamebookStyles.gb_para_padding}", "fontSize": "${gamebookStyles.gb_font_size}" }; page.items.push(obj); } function appendSentencePage(document) { var obj = { "type": "Sequence", "padding": "${gamebookStyles.gb_padding}", "items": [ ] }; var content = getContentItems(document); content[1].item[0].items.push(obj); } function appendChoicePage(document, text){ var obj = { "type": "Container", "padding": "${gamebookStyles.gb_padding}", "items": [ { "type": "Text", "id": "para_choice", "text": text, "speech": text, "color": "${gamebookStyles.gb_font_color}", "paddingBottom": "${gamebookStyles.gb_para_padding}", "fontSize": "${gamebookStyles.gb_font_size}" } ] }; var content = getContentItems(document); content[1].item[0].items.push(obj); } function appendChoice(document, id, text, argument){ const number_text = [ '①', '②', '③', '④', '⑤', '⑥', '⑦', '⑧', '⑨' ]; var page = getChoicePage(document); if( page == null ) return; var index = page.items.length - 1; var obj = { "type": "TouchWrapper", "id": id, "item": { "type": "Text", "text": number_text[index] + " " + text, "speech": String(index + 1) + "、" + text, "color": "${gamebookStyles.gb_font_color}", "fontSize": "${gamebookStyles.gb_font_size}" }, "onPress": [ { "type": "SendEvent", "arguments": [ argument ] } ] }; page.items.push(obj); } function setVideo(document, intent, video, argument){ document.mainTemplate.items[0].items[0].id = intent; document.mainTemplate.items[0].items[0].source = video; document.mainTemplate.items[0].items[0].onEnd[0].arguments.push(argument); } function setVideoTitle(document, title) { document.mainTemplate.items[0].items[1].text = title; } ・app.intent("ChoiceSelectIntent", async (handlerInput) => {  ユーザが選択肢(1とか2とか)を言ったときに呼ばれます。 ・app.intent("ChoiceNextIntent", async (handlerInput) => {  ユーザが「次へ」と言ったときに呼ばれます。 ・app.intent('LaunchRequest', async (handlerInput) => {   このスキル「テストゲームブック」が起動したときに呼ばれます。 ・app.intent('StopIntent', async (handlerInput) => {   このスキルが終了したときに呼ばれます。 ・app.intent('GameStartIntent', async (handlerInput) => {   ユーザが「始める」と言ったときに呼ばれます。 ・app.userEvent(undefined, async (handlerInput) => {   名前なしのコンポーネントでSendEventが呼ばれたときに呼ばれます。   通常は、名前付きのコンポーネントなのですが、onMountなどの名前なしの場所から呼ばれる場合に使われますが、今回は使っていません。 ・app.userEvent(CHOICE_INTENT, async (handlerInput) => {   名前付きのコンポーネントでSendEventが呼ばれたときに呼ばれます。 その他細かなAlexaに関連する処理は以下で実装しています。  /api/helpers/alexa-utils.js ご参考  SwaggerでLambdaのデバッグ環境を作る(4):Alexaをデバッグする 画面は、以下にテンプレートとして作成しておいたので、シナリオファイルの内容に従って、シーンごとに書き換えています。これが先ほどのAPLオーサリングツールで作成したものです。 /api/controllers/apl-gamebook/gamebookDocumentBase.json /api/controllers/apl-gamebook/gamebookVideoDocumentBase.json もし、フォントの大きさや文字色などのスタイルを変えたい場合は以下にまとめておきましたので編集してください。 /api/controllers/apl-gamebook/styleResource.json Alexaシミュレータで動作を確認する alexa developer consoleに戻って、「テスト」タブを選択します。 そして、「スキルテストが有効になっているステージ」を「開発中」に選択しなおします。 あとはこんな感じで進みます。 ビデオ再生が終わると、 下側のダイアログの領域をスワイプすると、選択肢が現れます。 選択肢をクリックすれば、そのシーンに遷移します。 または、「1番」とか「2番」と言っても遷移できます。 最後にLambdaにアップロードする 以下のフォルダをLambdaにアップしてください。 /api/controllers/apl-gamebook/ ただし、npmモジュールである「ask-sdk-core」も使っていますので、このフォルダにインストールしてZIPで固めてからアップしてください。 > cd api/controllers/api-gamebook/ > npm init -y > npm install ask-sdk ask-sdk-core ask-sdk-model Lambdaへの切り替えは、alexa developer consoleのエンドポイントのところです。 使わせていただいたコンテンツ いらすとや https://www.irasutoya.com/ Pexels https://www.pexels.com/ja-jp/videos/ 以上
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

VPSにNode.jsをインストールする

[root@<ホスト名> ~]# curl -sL https://rpm.nodesource.com/setup_14.x | sudo bash - ## Installing the NodeSource Node.js 14.x repo... ## Inspecting system... + rpm -q --whatprovides redhat-release || rpm -q --whatprovides centos-release || rpm -q --whatprovides cloudlinux-release || rpm -q --whatprovides sl-release + uname -m ## Confirming "el7-x86_64" is supported... + curl -sLf -o /dev/null 'https://rpm.nodesource.com/pub_14.x/el/7/x86_64/nodesource-release-el7-1.noarch.rpm' ## Downloading release setup RPM... + mktemp + curl -sL -o '/tmp/tmp.XPYzdAXJk4' 'https://rpm.nodesource.com/pub_14.x/el/7/x86_64/nodesource-release-el7-1.noarch.rpm' ## Installing release setup RPM... + rpm -i --nosignature --force '/tmp/tmp.XPYzdAXJk4' ## Cleaning up... + rm -f '/tmp/tmp.XPYzdAXJk4' ## Checking for existing installations... + rpm -qa 'node|npm' | grep -v nodesource ## Run `sudo yum install -y nodejs` to install Node.js 14.x and npm. ## You may also need development tools to build native addons: sudo yum install gcc-c++ make ## To install the Yarn package manager, run: curl -sL https://dl.yarnpkg.com/rpm/yarn.repo | sudo tee /etc/yum.repos.d/yarn.repo sudo yum install yarn [root@<ホスト名> ~]# yum install nodejs Loaded plugins: fastestmirror, langpacks Loading mirror speeds from cached hostfile * base: ftp.iij.ad.jp * epel: ftp.riken.jp * extras: ftp.iij.ad.jp * updates: ty1.mirror.newmediaexpress.com nodesource | 2.5 kB 00:00:00 nodesource/x86_64/primary_db | 42 kB 00:00:00 Resolving Dependencies --> Running transaction check ---> Package nodejs.x86_64 2:14.17.1-1nodesource will be installed --> Finished Dependency Resolution Dependencies Resolved ============================================================================================================== Package Arch Version Repository Size ============================================================================================================== Installing: nodejs x86_64 2:14.17.1-1nodesource nodesource 32 M Transaction Summary ============================================================================================================== Install 1 Package Total download size: 32 M Installed size: 92 M Is this ok [y/d/N]: y Downloading packages: warning: /var/cache/yum/x86_64/7/nodesource/packages/nodejs-14.17.1-1nodesource.x86_64.rpm: Header V4 RSA/SHA512 Signature, key ID 34fa74dd: NOKEY Public key for nodejs-14.17.1-1nodesource.x86_64.rpm is not installed nodejs-14.17.1-1nodesource.x86_64.rpm | 32 MB 00:00:03 Retrieving key from file:///etc/pki/rpm-gpg/NODESOURCE-GPG-SIGNING-KEY-EL Importing GPG key 0x34FA74DD: Userid : "NodeSource <gpg-rpm@nodesource.com>" Fingerprint: 2e55 207a 95d9 944b 0cc9 3261 5ddb e8d4 34fa 74dd Package : nodesource-release-el7-1.noarch (installed) From : /etc/pki/rpm-gpg/NODESOURCE-GPG-SIGNING-KEY-EL Is this ok [y/N]: y Running transaction check Running transaction test Transaction test succeeded Running transaction Warning: RPMDB altered outside of yum. Installing : 2:nodejs-14.17.1-1nodesource.x86_64 1/1 Verifying : 2:nodejs-14.17.1-1nodesource.x86_64 1/1 Installed: nodejs.x86_64 2:14.17.1-1nodesource Complete! [root@<ホスト名> ~]# node -v v14.17.1 [root@<ホスト名> ~]# npm -v 6.14.13
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む