tag:crieit.net,2005:https://crieit.net/tags/Node.js/feed 「Node.js」の記事 - Crieit Crieitでタグ「Node.js」に投稿された最近の記事 2023-02-05T21:40:36+09:00 https://crieit.net/tags/Node.js/feed tag:crieit.net,2005:PublicArticle/18382 2023-02-04T02:06:05+09:00 2023-02-05T21:40:36+09:00 https://crieit.net/posts/Playwright Playwright のミニマムなテスト。 <p><a target="_blank" rel="nofollow noopener" href="https://rentry.co/c53xn">https://rentry.co/c53xn</a></p> <h2 id="Playwright とはなにか。"><a href="#Playwright+%E3%81%A8%E3%81%AF%E3%81%AA%E3%81%AB%E3%81%8B%E3%80%82">Playwright とはなにか。</a></h2> <p><a target="_blank" rel="nofollow noopener" href="https://www.perplexity.ai/?s=u&uuid=69eed2a2-ebf2-41e1-9ef4-e0ef7b608826">Microsoft Playwright とは?</a></p> <p>Selenium みたいなもの。<br /> <a target="_blank" rel="nofollow noopener" href="https://www.selenium.dev/">https://www.selenium.dev/</a></p> <p>Puppeteer みたいなもの。<br /> <a target="_blank" rel="nofollow noopener" href="https://pptr.dev/">https://pptr.dev/</a><br /> <a target="_blank" rel="nofollow noopener" href="https://youtu.be/ARt3zDHSsd4?t=70">https://youtu.be/ARt3zDHSsd4?t=70</a></p> <p>簡単に言うと、ブラウザを使って、javascript が動作することでページがちゃんと見えるうなうなサイトをスクレイピングしたりするのに使うプログラムのセットのようなもの。<br /> ブラウザのプログラムが別に必要だが、パッケージマネージャーでインストールするタイプのものだと、セットでインストールすることもできる。</p> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/microsoft/playwright">https://github.com/microsoft/playwright</a></p> <h2 id="Node.js と npm"><a href="#Node.js+%E3%81%A8+npm">Node.js と npm</a></h2> <p>前準備として node.js をインストールして、npm パッケージマネージャ―が使える状態にする。<br /> 以降は、 node.js のインストールが完了されている状態での操作になる。</p> <p>node.js インストールの手順は省略<br /> <a target="_blank" rel="nofollow noopener" href="https://nodejs.org/ja/download/">https://nodejs.org/ja/download/</a></p> <p>Node.js をインストールすると npm パッケージマネージャーが使えます。<br /> Node.js 向けのモジュールは npm パッケージマネージャーのコマンドでインストールします。<br /> npm を使って、別のパッケージマネージャーをインストールすることができます。ですが、この記事では使用しません。</p> <h3 id="npm と node.js のバージョン。"><a href="#npm+%E3%81%A8+node.js+%E3%81%AE%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3%E3%80%82">npm と node.js のバージョン。</a></h3> <pre><code>npm -v 9.3.1 </code></pre> <pre><code>node -v v19.5.0 </code></pre> <h2 id="ブラウザを立ち上げて手動で操作して、その過程をコードで記録する。 test &amp; codegen"><a href="#%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E3%82%92%E7%AB%8B%E3%81%A1%E4%B8%8A%E3%81%92%E3%81%A6%E6%89%8B%E5%8B%95%E3%81%A7%E6%93%8D%E4%BD%9C%E3%81%97%E3%81%A6%E3%80%81%E3%81%9D%E3%81%AE%E9%81%8E%E7%A8%8B%E3%82%92%E3%82%B3%E3%83%BC%E3%83%89%E3%81%A7%E8%A8%98%E9%8C%B2%E3%81%99%E3%82%8B%E3%80%82+test+%26amp%3B+codegen">ブラウザを立ち上げて手動で操作して、その過程をコードで記録する。 test & codegen</a></h2> <p>手動でページ移動などの操作をして、コードとして記録する。</p> <p><a target="_blank" rel="nofollow noopener" href="https://youtu.be/wGr5rz8WGCE?t=130">https://youtu.be/wGr5rz8WGCE?t=130</a></p> <p>前準備として node.js をインストールして、npm パッケージマネージャ―が使える状態にする。<br /> <a target="_blank" rel="nofollow noopener" href="https://nodejs.org/ja/download/">https://nodejs.org/ja/download/</a></p> <p>Playwright のインストール。chromium ブラウザーも同時にインストールされる。</p> <pre><code class="terminal">npm i -D playwright </code></pre> <p><code>https://www.youtube.com/</code> を chromium ブラウザーで開いて、操作する。</p> <pre><code class="terminal"> npx playwright codegen https://www.youtube.com/ </code></pre> <p>chromium ブラウザーのウィンドウが2つ開いて、</p> <p><a href="https://crieit.now.sh/upload_images/34464d43dbe38897d5407b786822f21463dd3c0dba9c0.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/34464d43dbe38897d5407b786822f21463dd3c0dba9c0.png?mw=700" alt="image" /></a></p> <p>ブラウザ上の操作が記録されていく。<br /> <a href="https://crieit.now.sh/upload_images/906eb0fbc7e9c21bb271a4ca2788fc0763dd3c25e81ad.JPG" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/906eb0fbc7e9c21bb271a4ca2788fc0763dd3c25e81ad.JPG?mw=700" alt="image" /></a></p> <p>手動でブラウザでテキスト入力したり、ボタンを押したりしたりしたことがコードとして記録されるので、このことを利用して、記録されたコードをコピーして自動処理のプログラムコードにペーストして編集すれば期待する動作のプログラムのコーディングに役立つ。</p> <h2 id="node.js での実行"><a href="#node.js+%E3%81%A7%E3%81%AE%E5%AE%9F%E8%A1%8C">node.js での実行</a></h2> <p>javascript のプログラムファイルをつくり、Node.js で js ファイルを実行する。</p> <p><a target="_blank" rel="nofollow noopener" href="https://www.youtube.com/watch?v=2_BPIA5RgXU&list=PLkiSKDHzztLAChyWRdYyFcIM-_yJTjrsR">https://www.youtube.com/watch?v=2_BPIA5RgXU&list=PLkiSKDHzztLAChyWRdYyFcIM-_yJTjrsR</a></p> <p>まず、これから作るプログラムのプロジェクト用のディレクトリをつくる。</p> <pre><code class="terminal">mkdir playw_test cd playw_test </code></pre> <h3 id="package.json"><a href="#package.json">package.json</a></h3> <p>package.json というファイルを作って、そこにインストールしたいパッケージとバージョンを JSON 書式で書く。<br /> <a target="_blank" rel="nofollow noopener" href="https://www.startup-cloud.co.jp/liberty-technology/PublicItems/npm/package.json.html">https://www.startup-cloud.co.jp/liberty-technology/PublicItems/npm/package.json.html</a></p> <p>package.json<br /> <a target="_blank" rel="nofollow noopener" href="https://rentry.co/5gm25u">https://rentry.co/5gm25u</a></p> <pre><code class="javascript">{ "devDependencies": { "playwright": "1.30.0" } } </code></pre> <p>npm パッケージマネージャーで <code>install</code> コマンドを実行すると、package.json に記載されたパッケージがインストールされる。</p> <pre><code class="terminal">npm install </code></pre> <h3 id="playtest.js"><a href="#playtest.js">playtest.js</a></h3> <p><a target="_blank" rel="nofollow noopener" href="https://rentry.co/zga9c">https://rentry.co/zga9c</a></p> <pre><code class="javascript"><br />/** * node.js から playwright での chrome ブラウザ操作テスト **/ const playwright = require('playwright'); const url = "https://www.youtube.com/"; (async () => { const browser = await playwright.chromium.launch({ channel: 'chrome',headless : false, }); //context const context = await browser.newContext(); //page const page = await context.newPage(); //navigate await page.goto(url); var date = Date.now(); //screenshot await page.screenshot({path: `${date}.png`}); await browser.close(); })(); </code></pre> <p>気をつけないといけないのは、メソッド全てに <code>await</code>キーワードをつけないと、そんなメソッドはないですというそっけないエラーになるので、<code>await</code> を忘れるとあるはずのメソッドが無いということでどこで何がおかしいのかわからなくなる。</p> <p>Node.js での実行。</p> <pre><code class="terminal">playtw_test> node ./playtest.js </code></pre> <p>コマンドを実行して playtest.js が実行されると、ブラウザが立ちあがり、サイトに移動して、スクリーンキャプチャを保存して、ブラウザが閉じる。<br /> <a href="https://crieit.now.sh/upload_images/9988fa50fbfaecad1fbc68027522ea2b63de3d58e0028.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/9988fa50fbfaecad1fbc68027522ea2b63de3d58e0028.png?mw=700" alt="image" /></a></p> <h3 id="test"><a href="#test">test</a></h3> <p>playtw_test> playtest_2.js<br /> <a target="_blank" rel="nofollow noopener" href="https://rentry.co/2iui8">https://rentry.co/2iui8</a></p> <pre><code class="javascript">const playwright = require('playwright'); let url = ``; for( i = 2;i < process.argv.length; i++) { url = process.argv[i] ; } (async () => { const browser = await playwright.chromium.launch({ channel: 'chrome',headless : false, }); //context const context = await browser.newContext(); //page const page = await context.newPage(); //navigate await page.goto(url); await page.getByPlaceholder('検索').click(); await page.getByPlaceholder('検索').fill('鳥インフルエンザ'); await page.getByPlaceholder('検索').press('Enter'); await page.waitForTimeout(1000); //screenshot var date = Date.now(); await page.screenshot({path: `${date}.png`}); await browser.close(); })(); </code></pre> <p>playtest_2.js を node.js で実行。</p> <pre><code class="terminal">playtw_test> node ./playtest_2.js http://www.youtube.com/ </code></pre> <p><a href="https://crieit.now.sh/upload_images/40628d54304b41b9e98ce4376ca3cde563de43c4163d4.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/40628d54304b41b9e98ce4376ca3cde563de43c4163d4.png?mw=700" alt="image" /></a></p> tomato tag:crieit.net,2005:PublicArticle/18370 2023-01-19T02:53:19+09:00 2023-03-14T14:31:14+09:00 https://crieit.net/posts/AI-Skeb AIイラストもOKの依頼サイトをつくってる <p>タイトルの通りAIイラストもOKのイラストお題箱サイトを開発しています。そろそろ1ヶ月が経ちますがまだ完成しません。</p> <p><a target="_blank" rel="nofollow noopener" href="https://prompton.io">https://prompton.io</a></p> <p>↑ 既に投稿機能とリクエスト機能が使えるので遊びに来て!</p> <p><a href="https://crieit.now.sh/upload_images/4e09ec8c232bfbcd456874ece53299f063c832b434e31.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/4e09ec8c232bfbcd456874ece53299f063c832b434e31.jpg?mw=700" alt="Screenshot 2023-01-19 at 2.55.17.jpg" /></a></p> <h1 id="AIイラストとは?"><a href="#AI%E3%82%A4%E3%83%A9%E3%82%B9%E3%83%88%E3%81%A8%E3%81%AF%EF%BC%9F">AIイラストとは?</a></h1> <p>ここ数ヶ月で、HolaraAIやMidjourney、NovelAIなど色々な学習モデルが登場しており、各モデルもバージョンを上げて日々進化しています。最近ではカメラで撮った写真のような画像も生成できます。</p> <p>例えばこれは海底をイメージした画像を生成しました。</p> <p><a href="https://crieit.now.sh/upload_images/636001b7aa16d83c22abfe50895e632e63c81a7ab2bb1.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/636001b7aa16d83c22abfe50895e632e63c81a7ab2bb1.png?mw=700" alt="photograph_blur_dark_blue_seabed_pale_pink_slimes_dc526ba4-146f-4954-845f-ac0968dca5d1.png" /></a></p> <p><a href="https://crieit.now.sh/upload_images/532e24d505d2690cd7fea048e5182c4063c817ccad28b.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/532e24d505d2690cd7fea048e5182c4063c817ccad28b.png?mw=700" alt="photograph_blur_dark_blue_seabed_pale_pink_jellyfishes_3093a9f7-1937-472c-80fb-8e69ea76a4fe.png" /></a></p> <p>漫画のコマのようなイラストも生成できます。実際にAI漫画を公開したり売ってる人もいます。</p> <p><a href="https://crieit.now.sh/upload_images/a890b17611656bcf8d4b02c9a794e42263c82f9613498.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/a890b17611656bcf8d4b02c9a794e42263c82f9613498.png?mw=700" alt="__female_cute_face_beautiful_lip_manga_monochrome_dress_shirts__3b602eb4-c68f-4027-b6a0-0f8ffb8a3142.png" /></a></p> <p>好みですが、少し画質の悪いアニメのような画像も。</p> <p><a href="https://crieit.now.sh/upload_images/c75f44e61472d5e83a8fc4b15b5b56fd63c83004c6b07.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/c75f44e61472d5e83a8fc4b15b5b56fd63c83004c6b07.png?mw=700" alt="_1980s_anime_movie_still_pastel_color_scheme_1girl_blue_short_h_542ae44e-5d31-46b4-81e7-92c54be73ac5.png" /></a></p> <p>逆に落書きのような画像を生成することもできます。用途は不明です。</p> <p><a href="https://crieit.now.sh/upload_images/ef08554857ab4ca31afef639efb9145463b66d013ba28.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/ef08554857ab4ca31afef639efb9145463b66d013ba28.png?mw=700" alt="unnamed.png" /></a></p> <h1 id="何に使える?"><a href="#%E4%BD%95%E3%81%AB%E4%BD%BF%E3%81%88%E3%82%8B%EF%BC%9F">何に使える?</a></h1> <p>AIイラストを使えば、例えば、サイトや小説の挿絵がたくさん作れます。ポスターにも使えそうです。</p> <p><a href="https://crieit.now.sh/upload_images/e899008a67cd35384206b931a31acb8963b662db84f1c.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e899008a67cd35384206b931a31acb8963b662db84f1c.png?mw=700" alt="watercolor_female__short_hair_5bc29367-1bbd-404e-a07e-00290f84ca36.png" /></a></p> <p><a target="_blank" rel="nofollow noopener" href="https://prompton.io/about">https://prompton.io/about</a></p> <p>↑ このページの挿絵は全て「にじじゃーにー」で生成しています。</p> <h3 id="アイコンにも!"><a href="#%E3%82%A2%E3%82%A4%E3%82%B3%E3%83%B3%E3%81%AB%E3%82%82%EF%BC%81">アイコンにも!</a></h3> <p>それからYouTubeやTwitterのアイコンも作れます。</p> <p><a href="https://crieit.now.sh/upload_images/ef08554857ab4ca31afef639efb9145463c81fba94cb1.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/ef08554857ab4ca31afef639efb9145463c81fba94cb1.png?mw=700" alt="unnamed.png" /></a></p> <p>もっとシンプルなアイコンも作れます。</p> <p><a href="https://crieit.now.sh/upload_images/36408ee4536380afb28f233d9a47504863c8205b5d4eb.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/36408ee4536380afb28f233d9a47504863c8205b5d4eb.png?mw=700" alt="unnamed-2.png" /></a></p> <p>プロンプトに「selfie」を含めると自撮りみたいなアイコンも作れます。</p> <p><a href="https://crieit.now.sh/upload_images/5eb3a6e9c3cef8bf9ea2c8fbe407cd0d63c824bfe0ac2.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5eb3a6e9c3cef8bf9ea2c8fbe407cd0d63c824bfe0ac2.png?mw=700" alt="_instagram_photography_selfie_Japanese_female_high-school_stude_cffb9606-82e3-4189-bde8-7054f9e2fd44.png" /></a></p> <h3 id="バッグや服も"><a href="#%E3%83%90%E3%83%83%E3%82%B0%E3%82%84%E6%9C%8D%E3%82%82">バッグや服も</a></h3> <p>デザインの知識がある人なら服やバッグのデザインもできるかもしれません。</p> <p>自分はミニバッグがよく分かってないのですが、これは適当に作ってみた画像です。</p> <p><a href="https://crieit.now.sh/upload_images/fd9929ee0da3d3317731dbc4a2cf2c1563c822413fcf0.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/fd9929ee0da3d3317731dbc4a2cf2c1563c822413fcf0.png?mw=700" alt="unnamed-3.png" /></a></p> <p>これは適当に作った中学生が着てそうな服です。</p> <p><a href="https://crieit.now.sh/upload_images/3611c6f13b87eb1ce0e8876ae1014b1763c82624803f2.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/3611c6f13b87eb1ce0e8876ae1014b1763c82624803f2.png?mw=700" alt="_oversized_regular_white_hoodie_concept_with_one_black_line_adi_dc43a2a4-70ee-458e-ba1b-3ad3d1ab4282.png" /></a></p> <h1 id="今後は?"><a href="#%E4%BB%8A%E5%BE%8C%E3%81%AF%EF%BC%9F">今後は?</a></h1> <p>こういったAIツールや学習モデルは、毎日のように新しいものが公開されています。これからアニメだったり3Dモデルだったりの生成も可能になってくると勝手に予想しています。</p> <p><a target="_blank" rel="nofollow noopener" href="https://huggingface.co/gsdf/Counterfeit-V2.0">https://huggingface.co/gsdf/Counterfeit-V2.0</a></p> <p>↑ 最近だとこれ凄いアニメっぽい</p> <p>一眼レフからスマホカメラになってライトユーザが増えたように、絵本や漫画やアニメ、カードゲーム、ボードゲームといった創作がもっと身近になると感じてます。</p> <p>アイデアはあるけど時間がないって人はぜひAIイラストを触ってみてください。</p> <h1 id="リクエストして!"><a href="#%E3%83%AA%E3%82%AF%E3%82%A8%E3%82%B9%E3%83%88%E3%81%97%E3%81%A6%EF%BC%81">リクエストして!</a></h1> <p>今のところ、AIイラストだけでも、GPUの購入、プロンプトの組み合わせ、学習モデルの選定、マージモデル、Pythonでのコーディングなど、そこそこ難易度はあがります。</p> <p>そこで商用利用が可能なイラストを1枚500円からリクエストできるサイトを開発しています。</p> <p>AIイラストは複雑な要望に向いていないのでココナラのような業務委託には向いていませんが、代わりに短期間で沢山つくって納品することができます。500円x4枚 = 2000円のようなリクエストもできます。</p> <p><a href="https://crieit.now.sh/upload_images/1bf9011d1068ab495f0bc6ad86ae3f5763c833247e187.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/1bf9011d1068ab495f0bc6ad86ae3f5763c833247e187.jpg?mw=700" alt="Screenshot 2023-01-19 at 2.56.59.jpg" /></a></p> <p>無償リクエストの場合は、納品された画像は商用利用はできませんがアイコンなど好きに使えます。</p> <h3 id="そのほか"><a href="#%E3%81%9D%E3%81%AE%E3%81%BB%E3%81%8B">そのほか</a></h3> <p>こちらのユーザにもリクエストできるので、気になるからは見てみてください。</p> <p><a target="_blank" rel="nofollow noopener" href="https://prompton.io/9Gjz1zJZS1M41ddmaU7aFU1HNqC2">https://prompton.io/9Gjz1zJZS1M41ddmaU7aFU1HNqC2</a></p> <p>↑ かわいいアイコンはこちらに</p> <p><a target="_blank" rel="nofollow noopener" href="https://prompton.io/OMO61PwlcBWx5CVlVepXGP5U0gT2">https://prompton.io/OMO61PwlcBWx5CVlVepXGP5U0gT2</a></p> <p>↑ ロゴみたいなイラストはこちらに</p> <p><a target="_blank" rel="nofollow noopener" href="https://prompton.io/5FlXKGvMsSNi9QiyNTqkG2tFEmF3">https://prompton.io/5FlXKGvMsSNi9QiyNTqkG2tFEmF3</a></p> <p>↑ 女性向けのかっこいいイラストはこちらに</p> <p><a target="_blank" rel="nofollow noopener" href="https://prompton.io/OltOK5T6sZOuhVNay323tZkD2J32">https://prompton.io/OltOK5T6sZOuhVNay323tZkD2J32</a></p> <p>↑ カードゲームのようなかっこいいイラストはこちらに</p> プロンプトン tag:crieit.net,2005:PublicArticle/18278 2022-08-13T18:55:09+09:00 2022-08-13T18:55:09+09:00 https://crieit.net/posts/install-nodejs-using-asdf 【node/yarn】asdfでnodeをインストールし、Yarnもつかえるようにする <p>こんにちは、しきゆらです。</p> <p>今回は、<a target="_blank" rel="nofollow noopener" href="https://shikiyura.com/2022/08/install_the_multiple-runtime-versions_management_tool__asdf/">こちらで紹介したasdf</a>を使ってNode.jsをインストールし、個人的によく使っているYarnも使えるように環境を作っていきます。</p> <p>なお、Node.jsのインストールはすぐ終わったのですがYarnのインストールとか諸々に至るまでに紆余曲折ありました。まずは、インストールして環境構築までの流れをまとめます。</p> <p>その後に、紆余曲折部分をメモしておきます。<br /> 必要なコマンドだけよこせ!という方は、以下の「環境構築の流れ」を参考にしてみてください。</p> <h2 id="環境構築の流れ"><a href="#%E7%92%B0%E5%A2%83%E6%A7%8B%E7%AF%89%E3%81%AE%E6%B5%81%E3%82%8C">環境構築の流れ</a></h2> <p>コマンド自体の使い方等はそれぞれのツールのサイト等で確認してください。</p> <pre><code class="zsh"># node用のプラグインを追加 asdf plugin add nodejs # nodeをインストール asdf install nodejs latest # システム全体で使うバージョンを指定 asdf global nodejs latest # yarnを使う準備 corepack enable # asdfの再構築 asdf reshim nodejs </code></pre> <p>これにて、環境構築は終了です。</p> <p>yarnの手順が以前とだいぶ変わりましたが、npmで個別にインストールせずにパッケージマネージャを管理する仕組みが入ったようです。<br /> そのおかげで手順がさっぱりしました。</p> <p>では、前述の通り環境構築の紆余曲折や各ドキュメントを見ながらの細かい話は以下にまとめていきます。</p> <h2 id="環境構築の紆余曲折"><a href="#%E7%92%B0%E5%A2%83%E6%A7%8B%E7%AF%89%E3%81%AE%E7%B4%86%E4%BD%99%E6%9B%B2%E6%8A%98">環境構築の紆余曲折</a></h2> <h3 id="Nodeのインストール"><a href="#Node%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB">Nodeのインストール</a></h3> <p>こちらは、asdfの仕組みにのっとって進めるだけなので、<a target="_blank" rel="nofollow noopener" href="https://shikiyura.com/2022/08/install_the_multiple-runtime-versions_management_tool__asdf/">前回の記事</a>と同じ手順を踏むだけです。</p> <pre><code class="zsh"># node用のプラグインを追加 asdf plugin add nodejs # nodeをインストール asdf install nodejs latest # システム全体で使うバージョンを指定 asdf global nodejs latest # 確認 node -v </code></pre> <h3 id="Yarnのインストール"><a href="#Yarn%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB">Yarnのインストール</a></h3> <p>手間取ったのはこちらのほう。<br /> インストールするために、公式サイトを見てみると記述がガラッと変わっていました。<br /> <a target="_blank" rel="nofollow noopener" href="https://yarnpkg.com/getting-started/install">https://yarnpkg.com/getting-started/install</a></p> <p>Node.js 16.10以上だと以下のコマンドだけでよいとのこと。</p> <pre><code class="zsh">corepack enable </code></pre> <p>corepackとは何ぞや。<br /> と思って調べると、Node.jsのパッケージマネージャを管理するための仕組みのようです。<br /> <a target="_blank" rel="nofollow noopener" href="https://nodejs.org/api/corepack.html">https://nodejs.org/api/corepack.html</a></p> <p>いい感じにまとめてくれている方がいました。先人に感謝です。<br /> <a target="_blank" rel="nofollow noopener" href="https://zenn.dev/teppeis/articles/2021-05-corepack">https://zenn.dev/teppeis/articles/2021-05-corepack</a></p> <p>ということで、公式の手順に従ってコマンド実行してみました。</p> <pre><code class="zsh"># corepackを有効化 corepack enable # yarnは入った・・・? yarn zsh: command not found: yarn </code></pre> <p>ということで、実行してもyarnは使えませんでした。</p> <h3 id="corepackについてもう少し調べてみる"><a href="#corepack%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6%E3%82%82%E3%81%86%E5%B0%91%E3%81%97%E8%AA%BF%E3%81%B9%E3%81%A6%E3%81%BF%E3%82%8B">corepackについてもう少し調べてみる</a></h3> <p>なんでじゃ、と思ってこれまた調べてみると、プロジェクトごとに使うパッケージマネージャを指定するような使い方を想定しているようです。</p> <p>そして、事前に使うパッケージマネージャを指定することもできるようです。<br /> ということで、以下のコマンドで明示的にyarnを使うようにしてみます。</p> <pre><code class="zsh"># 明示的にyarnを使う corepack prepare --activate [email protected] # yarnは入った・・・? yarn zsh: command not found: yarn </code></pre> <p>同じくコマンドがない、と怒られたのでいろいろ考えてみました。<br /> ここで思い出したのが、rbenvではrehashというコマンドを使っていたこと。<br /> gemや新しいバージョンのRubyをインストールしたときはとりあえずrehashしておけ、<br /> という程度の認識でしたが、このような仕組みがasdfにもあるのでは、と思い調べてみました。</p> <h3 id="asdf reshim"><a href="#asdf+reshim">asdf reshim</a></h3> <p>調べてみると、ドンピシャな質問と回答が出てきました。<br /> <a target="_blank" rel="nofollow noopener" href="https://stackoverflow.com/questions/70082424/command-not-found-corepack-when-installing-yarn-on-node-v17-0-1">https://stackoverflow.com/questions/70082424/command-not-found-corepack-when-installing-yarn-on-node-v17-0-1</a></p> <p>asdf reshim <name>というコマンドでrbenv rehashのようなことができるとのこと。<br /> <a target="_blank" rel="nofollow noopener" href="https://asdf-vm.com/manage/core.html#reshim">https://asdf-vm.com/manage/core.html#reshim</a></p> <p>上記によると、インストール時に~/.asdf/shimsへコマンドとして使えるようにファイルが自動的に生成されるが、それ以外ではファイルが生成されないのこと。今回の場合は、nodeインストール後に手動でcorepack enableやcorepack prepare --activate [email protected]を実行しているのでasdf的には存在を知らない。そこで、asdfに追加があったことを教えてあげて、ファイルを作ってもらう必要があったようだ。</p> <pre><code class="zsh"># asdfにファイル生成をしてもらう asdf reshim nodejs # 確認 yarn -v # =&gt; 3.2.2 </code></pre> <p>ようやく使えるようになりました。<br /> ただ、未確認ではありますが、asdf reshimの動きを見るとcorepack prepareは不要な気がします。<br /> ということで、「環境構築の流れ」では省いてます。</p> <h3 id="おまけ: rbenv rehashについて改めて確認してみる"><a href="#%E3%81%8A%E3%81%BE%E3%81%91%3A+rbenv+rehash%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6%E6%94%B9%E3%82%81%E3%81%A6%E7%A2%BA%E8%AA%8D%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B">おまけ: rbenv rehashについて改めて確認してみる</a></h3> <p>ここで、改めてrbenv rehashを調べてみました。<br /> <a target="_blank" rel="nofollow noopener" href="https://github.com/rbenv/rbenv#rbenv-rehash">https://github.com/rbenv/rbenv#rbenv-rehash</a></p> <p>御幣を恐れずにざっくり書けば、新しいgemやRubyをインストールした場合は「~/.rbenv/versions/*/bin/」にインストールされるが、PATHが通っているのは「~/.rbenv/shims」なのでそのままでは使うことはできません。<br /> そこで、バージョン切り替えやgemのコマンドなどを追加する場合は新たに「~/.rbenv/shims」へコピーしてあげる必要があるわけです。そのコマンドがrbenv rehashのようです。</p> <p>・・・とはいえ、いつの間にかrbenv installなどのタイミングで良しなにrbenv rehashが実行されるようになり明示的に実行する必要はなくなったようです。</p> <p>ということで、asdfも正式版がリリースされる頃にはもしかしたら今回のようなごたごたはなくなるかもしれないなぁ・・・ということを思いながら、諸々調べる機会を与えてくれて感謝しつつ紆余曲折のメモを終わります。</p> <h2 id="まとめ"><a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></h2> <p>今回は、asdfを使ってNode.jsをインストールし、パッケージマネージャのYarnを使えるように環境構築をしました。<br /> いつの間にかYarnのインストール方法が変わっていたり、asdfの仕組みに躓いたりしましたが、いろいろなことを調べたり知ることができたので満足です。</p> <p>というわけで、今回はここまで。<br /> おしまい</p> しきゆら tag:crieit.net,2005:PublicArticle/18244 2022-07-17T11:29:34+09:00 2022-07-17T11:29:34+09:00 https://crieit.net/posts/Windows-nvm-node-firebase-tools-Unexpected-token Windowsのnvmのnodeでfirebase-toolsを入れようとしたらUnexpected token '.' エラー <p><code>npm install -g firebase-tools</code> を実行してインストールしようとしたら下記のようなエラーが出た。</p> <pre><code class="plain">npm ERR! Unexpected token '.' </code></pre> <p>調べてみたらどうもWindowsのnvmのバージョン1.1.7以下を使っている場合に発生してしまうらしい。</p> <p>ということで</p> <ol> <li>nvm最新をインストール</li> <li>Nodeの該当バージョンをアンインストール</li> <li>Nodeの該当バージョンを再度インストール</li> </ol> <p>で解決するとのこと。nvmだけでなくNodeも入れ直さないといけないっぽい。</p> <p>参考)<br /> <a target="_blank" rel="nofollow noopener" href="https://github.com/firebase/firebase-tools/issues/4134">https://github.com/firebase/firebase-tools/issues/4134</a></p> だら@Crieit開発者 tag:crieit.net,2005:PublicArticle/18168 2022-04-13T14:42:49+09:00 2022-04-14T12:15:20+09:00 https://crieit.net/posts/Tiddlywiki-Android Tiddlywiki を Android で使う <p>Tiddlywiki とはどういうものかというと、情報カードみたいにセルを取り出せるブログまたは、 wiki みたいな <a target="_blank" rel="nofollow noopener" href="https://en.m.wikipedia.org/wiki/Zettelkasten"> Zettelkasten </a> てものです。</p> <p><a target="_blank" rel="nofollow noopener" href="https://tiddlywiki.com/">https://tiddlywiki.com/</a></p> <p>html ファイル 1 つだけで完結しているものも出来上がるので、github pages でおいておくこともできます。</p> <p><a target="_blank" rel="nofollow noopener" href="https://ja.m.wikipedia.org/wiki/TiddlyWiki">https://ja.m.wikipedia.org/wiki/TiddlyWiki</a></p> <hr /> <p>Tiddlywiki を android で termux アプリを使って node.js の <strong>web サーバー</strong>で使う場合について書きます。</p> <p><code>ctrl</code> キーが必要なので hacker's keyboard などの 仮想キーボードアプリが必要です。</p> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/klausw/hackerskeyboard">https://github.com/klausw/hackerskeyboard</a></p> <hr /> <ul> <li>まずはじめに node.js をインストールしておきます。</li> </ul> <p>pkg ( termux のパッケージマネージャーのコマンド )でインスールします。</p> <pre><code class="bash">pkg install nodejs </code></pre> <ul> <li>npm ( node.js のパッケージマネージャーの <code>npm</code> コマンド)を使ってTiddlywiki をインストール。</li> </ul> <pre><code class="bash"><br />u0_a185@localhost ~> npm install -g tiddlywiki added 1 package, and audited 2 packages in 17s found 0 vulnerabilities npm notice npm notice New minor version of npm available! 8.5.4 -> 8.6.0 npm notice Changelog: https://github.com/npm/cli/releases/tag/v8.6.0 npm notice Run npm install -g [email protected] to update! npm notice </code></pre> <ul> <li>local host (<code>127.0.0.1</code>) に <strong>mywik</strong>i という名前で、tiddlywiki (ウィキ)を作ってみます。</li> </ul> <pre><code class="bash">u0_a185@localhost ~> tiddlywiki mywiki --init server Copied edition 'server' to mywiki </code></pre> <ul> <li>local host ポート番号 <code>8080</code> に <strong>web サーバー</strong>を起ち上げます。</li> </ul> <pre><code class="bash">u0_a185@localhost ~> tiddlywiki mywiki --server 8080 syncer-server-filesystem: Dispatching 'save' task: $:/StoryList Serving on http://127.0.0.1:8080 (press ctrl-C to exit) </code></pre> <p>ブラウザのアドレスバーに、<code>127.0.0.1:8080</code> とタイプしてエンターします。<br /> あとはブラウザ上で、操作します。</p> <p>終わるときは node.js の <strong>webサーバー</strong>をストップさせます。</p> <p>termux のターミナル上で <code>ctrl</code> キーと <code>c</code> で node.js の <strong>web サーバー</strong>を止めます。</p> <p><a href="https://crieit.now.sh/upload_images/a43079fd953a0c294890492032a1256a62566931004e9.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/a43079fd953a0c294890492032a1256a62566931004e9.png?mw=700" alt="image" /></a></p> <p>plugin を追加するには、web サーバーを止めておいて、ターミナルから、作った <strong>mywiki</strong> ディレクトリにある <strong>tiddlywiki.info</strong> ファイルをテキストエディターで編集します。plugin 、は存在が確認できているものを tiddlywiki.info に書き足していくと、サーバーを起動してブラウザでアクセスすると反映されています。json 形式のファイルを編集したことがあれば、下のキャプチャした画像を見れば理解できるはずです。</p> <p>markdown のプラグインを追加している様子。<br /> <a href="https://crieit.now.sh/upload_images/4efab44c79645651adb643782c5a899f62567460080d1.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/4efab44c79645651adb643782c5a899f62567460080d1.png?mw=700" alt="image" /></a></p> <p><a target="_blank" rel="nofollow noopener" href="https://youtu.be/KsXCTbRmAHI">https://youtu.be/KsXCTbRmAHI</a></p> <hr /> tomato tag:crieit.net,2005:PublicArticle/18120 2022-02-21T21:08:44+09:00 2022-02-21T21:08:44+09:00 https://crieit.net/posts/GitHub-Actions-EACCES-permission-denied GitHub ActionsでEACCES: permission deniedが出るとき <p>GitHub Actionsでnpx prisma generateを実行するとEACCES: permission deniedというエラーが出て全然うまくいかない。</p> <p>ためしに yarn prisma generate とすると動いた。npxだとユーザーかなにかが違う…?</p> だら@Crieit開発者 tag:crieit.net,2005:PublicArticle/17931 2022-01-11T23:52:59+09:00 2022-01-11T23:52:59+09:00 https://crieit.net/posts/checker-of-colors-fakers-etc-packages-20220111 colors.js, faker.js 等を使用しているか調べる簡易チェッカー <p>先日ショッキングな commit が push されたというニュースを知りました。そこで念のため colors や faker の開発者が開発したパッケージを自分のプロジェクトが使用しているかを調べる簡易チェッカーを作ってみました。</p> <h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.bleepingcomputer.com/news/security/dev-corrupts-npm-libs-colors-and-faker-breaking-thousands-of-apps/">Dev corrupts NPM libs 'colors' and 'faker' breaking thousands of apps</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://zenn.dev/azu/articles/d56615b2e11ad1">colorsなどのnpmパッケージに悪意あるコードが含まれている問題について</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://twitter.com/jingbay/status/1480458265452740609">高梨陣平さんはTwitterを使っています 「開発者 Marak Squiresは自身のOSSを故意にサボタージュ版をリリース。問題の製品は“faker.js” と “colors.js”。両者共に“LIBERTY LIBERTY LIBERTY.”の3行で始まる無意味な文字列を無限に表示。両者は週250万DL、2240万DLで影響はでかい。NPMはrevertを行いSquiresはGitHubからアクセスを奪われた。Ve」 \/ Twitter</a> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://twitter.com/jingbay/status/1480458906300477441">高梨陣平さんはTwitterを使っています 「rgeはGitHubにコメントを求めたが返答はない。その後、Bleeping Computerが2020年11月にSquiresによるもう無料で仕事はしたくないとのGitHubへの投稿を発見している。Squiresの大胆な行動はOSS開発のモラルと経済的な問題に注目を集めている。」 \/ Twitter</a></li> </ul></li> <li><a target="_blank" rel="nofollow noopener" href="https://note.com/takahiroyte/n/nd6cceae3af04">OSSのゆく道:Faker.jsの顛末|Takahiro Ito|note</a></li> </ul> <p>やろうと思えばできてしまうのがオープンソース界隈のエコシステムの怖いところではあります。</p> <p>詳細は上述の記事をご参照ください。</p> <p>それよりも、目下気になるのは自分のプロジェクトでこれらの npmパッケージ を使用していないかどうか。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://gist.githubusercontent.com/azu/11b105a9e35dc9d5f07312c24a35c82b/raw/000a0c728ac1748b6fb3c80c8a0753fb1f6a57e8/marak-packages.md">https://gist.githubusercontent.com/azu/11b105a9e35dc9d5f07312c24a35c82b/raw/000a0c728ac1748b6fb3c80c8a0753fb1f6a57e8/marak-packages.md</a></li> </ul> <p>colors や faker を開発した方がメンテナーになっているパッケージ一覧を<a target="_blank" rel="nofollow noopener" href="https://gist.github.com/azu/">azu</a>氏が作ってくださったので、このリストを元にパッケージの使用を確認する簡易チェッカーを作ってみました。</p> <h2 id="成果物"><a href="#%E6%88%90%E6%9E%9C%E7%89%A9">成果物</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/arm-band/test-adramelek">GitHub - arm-band\/test-adramelek</a></li> </ul> <h2 id="コード (要点のみ)"><a href="#%E3%82%B3%E3%83%BC%E3%83%89+%28%E8%A6%81%E7%82%B9%E3%81%AE%E3%81%BF%29">コード (要点のみ)</a></h2> <pre><code class="js">const path = require('path'); const request = require('request'); const { spawnSync } = require('child_process'); const fs = require('fs'); const marakOwnedUrl = 'https://gist.githubusercontent.com/azu/11b105a9e35dc9d5f07312c24a35c82b/raw/000a0c728ac1748b6fb3c80c8a0753fb1f6a57e8/marak-packages.md'; // 略 request.get({ url: marakOwnedUrl, }, function (error, response, body) { const mdStream = body.split("\n"); const packageNamesArray = mdStream.filter((val) => { return val.length > 0 && !val.startsWith('http'); }); let cnt = { found: 0, notfound: 0, } let resultStr = `Project: ${projectPathStr} `; for (let i = 0; i < packageNamesArray.length; i++) { console.log(`yarn why ${packageNamesArray[i]}`); resultStr += `Package "${packageNamesArray[i]}": ${i + 1} / ${packageNamesArray.length} > yarn why ${packageNamesArray[i]} `; console.log(`package "${packageNamesArray[i]}": ${i + 1} / ${packageNamesArray.length}`); const spawn = spawnSync(`yarn why ${packageNamesArray[i]} --cwd ${projectPathStr}`, { shell: true }); console.log(spawn.stdout.toString()); resultStr += `${spawn.stdout.toString()} `; console.log(spawn.stderr.toString()); resultStr += `${spawn.stderr.toString()} `; spawn.stderr.toString().length > 0 ? cnt.notfound++ : cnt.found++; } const cntMsg = ` Found packages: ${cnt.found} Not found packages: ${cnt.notfound}`; console.log(cntMsg); resultStr += cntMsg; try{ fs.writeFileSync(path.join(path.join('.', 'dist'), `scan-result-${projectName}.log`), resultStr); } catch(e){ console.log(e.message); } }); </code></pre> <ul> <li>先程の mdファイル をHTTPリクエストで取得し、パッケージ名の行のみ(1行の文字列が0(改行のみ)と<code>http</code>始まりを除外)処理します</li> <li>パッケージ名分ループして、 <code>child_process</code> の <code>spawn</code> を使用し、 <code>yarn why {パッケージ名} --cwd {プロジェクトのパス}</code> コマンドを実行 (<code>npm</code> は今回は見ていません) <ul> <li>コマンドライン引数でプロジェクトのパスを指定することで、該当プロジェクトのディレクトリで <code>yarn why</code> をしています</li> </ul></li> <li>実行結果を <code>console.log</code> で表示しつつ、メモリに格納。全ての確認が終わるとテキストファイルにダンプします <ul> <li>標準出力エラー(パッケージが見付からなかった)と成功(パッケージが見付かった)をカウントして出力に付与しています</li> <li>標準出力エラーを使うために <code>spawn</code> を使用</li> </ul></li> </ul> <p>スピード重視でざっくり作りました。ひとまず自分の手元で動いて予期する動作になったので公開しました。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <h3 id="きっかけ"><a href="#%E3%81%8D%E3%81%A3%E3%81%8B%E3%81%91">きっかけ</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.bleepingcomputer.com/news/security/dev-corrupts-npm-libs-colors-and-faker-breaking-thousands-of-apps/">Dev corrupts NPM libs 'colors' and 'faker' breaking thousands of apps</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://zenn.dev/azu/articles/d56615b2e11ad1">colorsなどのnpmパッケージに悪意あるコードが含まれている問題について</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://twitter.com/jingbay/status/1480458265452740609">高梨陣平さんはTwitterを使っています 「開発者 Marak Squiresは自身のOSSを故意にサボタージュ版をリリース。問題の製品は“faker.js” と “colors.js”。両者共に“LIBERTY LIBERTY LIBERTY.”の3行で始まる無意味な文字列を無限に表示。両者は週250万DL、2240万DLで影響はでかい。NPMはrevertを行いSquiresはGitHubからアクセスを奪われた。Ve」 \/ Twitter</a> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://twitter.com/jingbay/status/1480458906300477441">高梨陣平さんはTwitterを使っています 「rgeはGitHubにコメントを求めたが返答はない。その後、Bleeping Computerが2020年11月にSquiresによるもう無料で仕事はしたくないとのGitHubへの投稿を発見している。Squiresの大胆な行動はOSS開発のモラルと経済的な問題に注目を集めている。」 \/ Twitter</a></li> </ul></li> <li><a target="_blank" rel="nofollow noopener" href="https://softantenna.com/blog/developer-corrupts-his-own-widely-used-library/">オープンソース開発者が広く使用されている自分のライブラリを改ざん、大量のプロジェクトに影響 | ソフトアンテナ</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/ginpei/items/a4eb012827f5a102ff4d">Firebase CLIとかの出力が2022-01-10の一時期に乱れてた件 - Qiita</a></li> </ul> <h4 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://note.com/takahiroyte/n/nd6cceae3af04">OSSのゆく道:Faker.jsの顛末|Takahiro Ito|note</a></li> </ul> <h4 id="前兆"><a href="#%E5%89%8D%E5%85%86">前兆</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://ledsun.hatenablog.com/entry/2022/01/09/090825">color.js の謎のissue - @ledsun blog</a></li> </ul> <h3 id="パッケージ一覧"><a href="#%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E4%B8%80%E8%A6%A7">パッケージ一覧</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://gist.githubusercontent.com/azu/11b105a9e35dc9d5f07312c24a35c82b/raw/000a0c728ac1748b6fb3c80c8a0753fb1f6a57e8/marak-packages.md">https://gist.githubusercontent.com/azu/11b105a9e35dc9d5f07312c24a35c82b/raw/000a0c728ac1748b6fb3c80c8a0753fb1f6a57e8/marak-packages.md</a></li> </ul> <h3 id="HTTPクライアント"><a href="#HTTP%E3%82%AF%E3%83%A9%E3%82%A4%E3%82%A2%E3%83%B3%E3%83%88">HTTPクライアント</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://garafu.blogspot.com/2017/05/node-http-httpss-request.html">Node.js で HTTP\/HTTPS リクエスト を行う方法 - galife</a></li> </ul> <h3 id="コマンド実行"><a href="#%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E5%AE%9F%E8%A1%8C">コマンド実行</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.wakuwakubank.com/posts/728-nodejs-child-process/">Node.js|シェルコマンドを実行する方法(child_process) - わくわくBank</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://ja.stackoverflow.com/questions/28527/node-js-%E3%81%AE-execsync-%E3%81%AE%E6%88%BB%E3%82%8A%E5%80%A4%E3%81%A7-stderr-%E3%82%92%E5%8F%97%E3%81%91%E5%8F%96%E3%82%8B%E6%96%B9%E6%B3%95">node.js の execSync の戻り値で stderr を受け取る方法 - スタック・オーバーフロー</a> <ul> <li><code>stderr</code> を使用するならば <code>spawn</code> の方が良い</li> </ul></li> <li><a target="_blank" rel="nofollow noopener" href="https://neos21.net/blog/2019/10/18-01.html#child_processspawnsync">Node.js の Child Process 研究 : fork・exec・execFile・spawn の違いをサンプルコードとともに検証 - Neo's World</a></li> </ul> <h3 id="path"><a href="#path">path</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/oblivion/items/e9677ef4ca38643aaa14">nodejsのpathモジュールの使い方 - Qiita</a></li> </ul> <h3 id="コマンドライン引数"><a href="#%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%83%A9%E3%82%A4%E3%83%B3%E5%BC%95%E6%95%B0">コマンドライン引数</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/furusin_oriver/items/f030d1eaa9e7b54233c3">Node.jsでコマンドライン引数を取得する - Qiita</a></li> </ul> <h3 id="ファイル・ディレクトリ存在確認"><a href="#%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%83%BB%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E5%AD%98%E5%9C%A8%E7%A2%BA%E8%AA%8D">ファイル・ディレクトリ存在確認</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.gesource.jp/weblog/?p=8213">node.jsでファイルやディレクトリが存在するかどうかを調べる – 山本隆の開発日誌</a></li> </ul> <h3 id="ファイル書き込み"><a href="#%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E6%9B%B8%E3%81%8D%E8%BE%BC%E3%81%BF">ファイル書き込み</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://blog.katsubemakito.net/nodejs/file-write">[Node.js] ファイルに書き込む様々な方法</a></li> </ul> <h3 id="標準出力"><a href="#%E6%A8%99%E6%BA%96%E5%87%BA%E5%8A%9B">標準出力</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://maku77.github.io/nodejs/io/stdio.html">標準出力 (stdout)、標準エラー出力 (stderr) への出力 | まくまくNode.jsノート</a></li> </ul> <h3 id="別ディレクトリで yarn 実行"><a href="#%E5%88%A5%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E3%81%A7+yarn+%E5%AE%9F%E8%A1%8C">別ディレクトリで yarn 実行</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://zenn.dev/hiro4hiro4/articles/cb193f71b2ad26">ルートのpackage.jsonから、yarnで作業ディレクトリを指定してscriptを実行する方法</a></li> </ul> <h3 id="(未使用) 現在ディレクトリの取得"><a href="#%28%E6%9C%AA%E4%BD%BF%E7%94%A8%29+%E7%8F%BE%E5%9C%A8%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E3%81%AE%E5%8F%96%E5%BE%97">(未使用) 現在ディレクトリの取得</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/Ancient_Scapes/items/6751461d8547200b6715">JavaScript\/Node.jsで現在のディレクトリ名のみを取得する - Qiita</a></li> </ul> <h3 id="(未使用) OS判別"><a href="#%28%E6%9C%AA%E4%BD%BF%E7%94%A8%29+OS%E5%88%A4%E5%88%A5">(未使用) OS判別</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://kantaro-cgi.com/blog/nodejs/nodejs-check-os.html">Node.jsで実行してるOSを判別する方法 - ノウハウブログ - カンタローCGI</a></li> </ul> <h3 id="(未使用) 文字化け"><a href="#%28%E6%9C%AA%E4%BD%BF%E7%94%A8%29+%E6%96%87%E5%AD%97%E5%8C%96%E3%81%91">(未使用) 文字化け</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://hacknote.jp/archives/11522/">node.jsのchild_processをWindowsで使う | ハックノート</a></li> </ul> <h3 id="ARRAy.filter()"><a href="#ARRAy.filter%28%29">ARRAy.filter()</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/filter">Array.prototype.filter() - JavaScript | MDN</a></li> </ul> <h3 id="(未使用) (コマンド) 現在ディレクトリの確認"><a href="#%28%E6%9C%AA%E4%BD%BF%E7%94%A8%29+%28%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%29+%E7%8F%BE%E5%9C%A8%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E3%81%AE%E7%A2%BA%E8%AA%8D">(未使用) (コマンド) 現在ディレクトリの確認</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://xtech.nikkei.com/it/atcl/column/15/042000103/042200009/">[cd・chdir \/ pwd]カレントディレクトリを知る | 日経クロステック(xTECH)</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/17872 2021-12-19T01:01:44+09:00 2021-12-19T01:01:44+09:00 https://crieit.net/posts/aws-ecs-fargate-amp-grafana 📔 ECS Fargate のメトリクスを Prometheus Agent 使って AMP に送って Grafana で監視する <h1 id="はじめに"><a href="#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB">はじめに</a></h1> <p><strong>この記事は <a target="_blank" rel="nofollow noopener" href="https://qiita.com/advent-calendar/2021/aws">AWS Advent Calendar 2021</a> の 5 日目の記事です。</strong></p> <p><a target="_blank" rel="nofollow noopener" href="https://aws.amazon.com/jp/fargate/">Fargate</a> で Node.js アプリのメトリクスを <a target="_blank" rel="nofollow noopener" href="https://prometheus.io/blog/2021/11/16/agent/">Prometheus Agent</a> をサイドカーコンテナとして動かして、<a target="_blank" rel="nofollow noopener" href="https://aws.amazon.com/jp/prometheus/">Amazon Managed Service for Prometheus (AMP)</a> に送信して <a target="_blank" rel="nofollow noopener" href="https://grafana.com/">Grafana</a> で見られるようにしてみました。</p> <p>ちなみに Promethus Agent はまだ <a target="_blank" rel="nofollow noopener" href="https://prometheus.io/blog/2021/11/16/agent/#prometheus-agent-mode">実験的な機能</a> なため、<strong>実務での利用は推奨しません。</strong></p> <p>本記事の環境構築には <a target="_blank" rel="nofollow noopener" href="https://aws.amazon.com/jp/cdk/">AWS CDK</a> を利用しています。</p> <h1 id="動作環境"><a href="#%E5%8B%95%E4%BD%9C%E7%92%B0%E5%A2%83">動作環境</a></h1> <ul> <li>Node.js v16.13.0</li> <li>AWS CDK 2.0.0 (build 4b6ce31)</li> <li>Prometheus 2.32.1</li> </ul> <h1 id="環境構築"><a href="#%E7%92%B0%E5%A2%83%E6%A7%8B%E7%AF%89">環境構築</a></h1> <h2 id="AWS CDK で環境構築する"><a href="#AWS+CDK+%E3%81%A7%E7%92%B0%E5%A2%83%E6%A7%8B%E7%AF%89%E3%81%99%E3%82%8B">AWS CDK で環境構築する</a></h2> <p>CDK で構築作業を進めます。まずは下記コマンドで CDK プロジェクトを作成します。使用言語は <code>TypeScript</code> を選択します。</p> <pre><code class="bash">mkdir prometheus-agent-test && cd prometheus-agent-test cdk init --language typescript </code></pre> <p>まず CDK でインフラ構築を進めていく前に、メトリクス収集テスト用の Node.js アプリを準備します。</p> <h3 id="ECS Fargate で動かす Node.js アプリを準備する"><a href="#ECS+Fargate+%E3%81%A7%E5%8B%95%E3%81%8B%E3%81%99+Node.js+%E3%82%A2%E3%83%97%E3%83%AA%E3%82%92%E6%BA%96%E5%82%99%E3%81%99%E3%82%8B">ECS Fargate で動かす Node.js アプリを準備する</a></h3> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/siimon/prom-client">prom-client</a> を利用して、Node.js のメトリクスが取得できるだけの Node.js アプリを準備します。<code>prometheus-agent-test</code> フォルダで下記コマンドを実行します。</p> <pre><code class="bash">mkdir metrics-app && cd metrics-app npm init -y npm install --save prom-client </code></pre> <p>次に <code>metrics-app</code> フォルダ内に <code>index.js</code> を作成して下記を編集します。</p> <pre><code class="javascript">// metrics-app/index.js "use strict"; const http = require("http"); const server = http.createServer(); const client = require("prom-client"); const register = new client.Registry(); // 5秒間隔でメトリクスを取得する client.collectDefaultMetrics({ register, timeout: 5 * 1000 }); server.on("request", async function (req, res) { // /metrics にアクセスしたら、Prometheus のレポートを返す if (req.url === "/metrics") { res.setHeader("Content-Type", register.contentType); const metrics = await register.metrics(); return res.end(metrics); } else { return res.writeHead(404, { "Content-Type": "text/plain" }); } }); server.listen(8080); </code></pre> <p><code>node index.js</code> コマンドを実行して <code>http://localhost:8080/metrics</code> にアクセスしてみます。下記のように各種メトリクスが出力されている様子が確認できれば OK です。</p> <p><img src="https://i.gyazo.com/a5feae6dba9a9f4eaecae0055dd9be9e.png" alt="Prometheus のレポートが正常に出力されている様子" /><br /> <strong>Prometheus のレポートが正常に出力されている様子</strong></p> <p>今回は ECS 上で Node.js アプリを動作させるため、<code>Dockerfile</code> も作成します。</p> <pre><code class="docker"># metrics-app/Dockerfile FROM public.ecr.aws/docker/library/node:16-alpine3.12 AS builder EXPOSE 8080 WORKDIR /usr/src/app COPY package*.json ./ RUN npm install --max-old-space-size=4096 COPY . . CMD [ "node", "index.js" ] </code></pre> <p>上記 <code>Dockerfile</code> 作成後、再び動作検証のため下記コマンドを実行してから、<code>http://localhost:8080/metrics</code> にアクセスしてみます。</p> <pre><code class="bash">docker build -t prometheus-agent-test/metrics-app . docker run -p 8080:8080 prometheus-agent-test/metrics-app:latest </code></pre> <p>先ほどと同様に <code>http://localhost:8080/metrics</code> アクセス時に各種メトリクスが出力されている様子を確認できれば OK です。</p> <h3 id="Node.js アプリを監視する Prometheus Agent を準備する"><a href="#Node.js+%E3%82%A2%E3%83%97%E3%83%AA%E3%82%92%E7%9B%A3%E8%A6%96%E3%81%99%E3%82%8B+Prometheus+Agent+%E3%82%92%E6%BA%96%E5%82%99%E3%81%99%E3%82%8B">Node.js アプリを監視する Prometheus Agent を準備する</a></h3> <p>まずは Prometheus 関連ファイルを配置するためのフォルダを作成します。<code>prometheus-agent-test</code> フォルダ内で下記コマンドを実行します。</p> <pre><code class="bash">mkdir prometheus-agent && cd prometheus-agent </code></pre> <p>次に Prometheus の設定テンプレートファイルを作成します。テンプレートファイルは <a target="_blank" rel="nofollow noopener" href="https://atmarkit.itmedia.co.jp/ait/articles/1610/18/news008.html"><code>sed</code></a> を利用して中身の <code>__TASK_ID__</code> および <code>__REMOTE_WRITE_URL__</code> を書き換えて利用します。</p> <pre><code class="yaml"># prometheus-agent/prometheus.tmpl.yml global: scrape_interval: 5s external_labels: monitor: "prometheus" scrape_configs: - job_name: "prometheus-agent-test" static_configs: - targets: ["localhost:8080"] labels: # デフォルトの localhost:8080 がインスタンスとして利用されると、 # メトリクスの判別がしづらくなるため ECS Task の ID を利用する instance: "__TASK_ID__" remote_write: # AMP ワークスペース作成時に控えておいた、 # `エンドポイント - リモート書き込み URL` を設定する箇所 - url: "__REMOTE_WRITE_URL__" sigv4: region: ap-northeast-1 queue_config: max_samples_per_send: 1000 max_shards: 200 capacity: 2500 </code></pre> <p>設定ファイルの作成が完了したら、テンプレートファイルを利用して Prometheus の設定ファイルを作成し、Prometheus Agent を起動させるためのシェルスクリプトを作成します。</p> <pre><code class="sh"># prometheus-agent/docker-entrypoint.sh #!/bin/sh while [ -z "$taskId" ] do # ECS Fargate で起動したタスク ID を取得する taskId=$(curl --silent ${ECS_CONTAINER_METADATA_URI}/task | jq -r '.TaskARN | split("/") | .[-1]') echo "waiting..." sleep 1 done echo "taskId: ${taskId}" echo "remoteWriteUrl: ${REMOTE_WRITE_URL}" # タスク ID `taskId` および、環境変数 `REMOTE_WRITE_URL` で、 # Prometheus のテンプレートファイル `prometheus.tmpl.yml` の内容を書き換え、 # その結果を `/etc/prometheus/prometheus.yml` に出力する cat /etc/prometheus/prometheus.tmpl.yml | \ sed "s/__TASK_ID__/${taskId}/g" | \ sed "s>__REMOTE_WRITE_URL__>${REMOTE_WRITE_URL}>g" > /etc/prometheus/prometheus.yml # --enable-feature=agent で Prometheus を Agent モードで起動する # Prometheus のコンフィグファイルには上記で出力した `/etc/prometheus/prometheus.yml` を利用する /usr/local/bin/prometheus \ --enable-feature=agent \ --config.file=/etc/prometheus/prometheus.yml \ --web.console.libraries=/etc/prometheus/console_libraries \ --web.console.templates=/etc/prometheus/consoles </code></pre> <p>これで Prometheus Agent 起動のための準備は整ったため、最後に <code>Dockerfile</code> を準備します。ちなみに Prometheus Agent は <code>v2.32.0</code> 以降で利用可能です。<strong>本記事では <code>v2.32.1</code> を利用します。</strong></p> <pre><code class="docker"># prometheus-agent/Dockerfile FROM --platform=arm64 alpine:3.15 ADD prometheus.tmpl.yml /etc/prometheus/ RUN apk add --update --no-cache jq sed curl # ARM64 で動作する Prometheus v2.32.1 を curl でダウンロード展開する RUN curl -sL -O https://github.com/prometheus/prometheus/releases/download/v2.32.1/prometheus-2.32.1.linux-arm64.tar.gz RUN tar -zxvf prometheus-2.32.1.linux-arm64.tar.gz && rm prometheus-2.32.1.linux-arm64.tar.gz # `prometheus` コマンドを `/usr/local/bin/prometheus` に移動する RUN mv prometheus-2.32.1.linux-arm64/prometheus /usr/local/bin/prometheus COPY ./docker-entrypoint.sh / RUN chmod +x /docker-entrypoint.sh CMD ["/docker-entrypoint.sh"] </code></pre> <p>ここまでで CDK でインフラ整備を進めていくための下準備は完了です。</p> <h3 id="ECS Fargate 上で Node.js アプリおよび Prometheus Agent を動作させる"><a href="#ECS+Fargate+%E4%B8%8A%E3%81%A7+Node.js+%E3%82%A2%E3%83%97%E3%83%AA%E3%81%8A%E3%82%88%E3%81%B3+Prometheus+Agent+%E3%82%92%E5%8B%95%E4%BD%9C%E3%81%95%E3%81%9B%E3%82%8B">ECS Fargate 上で Node.js アプリおよび Prometheus Agent を動作させる</a></h3> <p>あとは CDK で ECS Fargate 上で Node.js アプリおよび Prometheus Agent、Grafana を動作させるための環境を整備していきます。</p> <p><code>lib/prometheus-agent-test-stack.ts</code> の内容を書き換えます。</p> <pre><code class="typescript">// lib/prometheus-agent-test-stack.ts import { Construct } from "constructs"; import { Stack, StackProps, aws_ecs as ecs, aws_logs as logs, aws_aps as aps, aws_ecs_patterns as ecs_patterns, aws_iam as iam, aws_elasticloadbalancingv2 as elbv2, Duration, CfnOutput, } from "aws-cdk-lib"; export class PrometheusAgentTestStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); // Node.js アプリに ecs_patterns.ApplicationLoadBalancedFargateService を利用して ALB 経由でアクセス可能にする const projectName = "prometheus-agent-test"; const fargateService = new ecs_patterns.ApplicationLoadBalancedFargateService( this, `${projectName}-fargate-service`, { serviceName: `${projectName}-fargate-service`, cpu: 256, desiredCount: 3, listenerPort: 80, taskImageOptions: { family: `${projectName}-taskdef`, image: ecs.ContainerImage.fromAsset("metrics-app"), containerPort: 8080, logDriver: ecs.LogDrivers.awsLogs({ streamPrefix: `/${projectName}/metrics-app`, logRetention: logs.RetentionDays.ONE_DAY, }), }, cluster: new ecs.Cluster(this, `${projectName}-cluster`, { clusterName: `${projectName}-cluster`, }), memoryLimitMiB: 512, } ); fargateService.targetGroup.configureHealthCheck({ path: "/metrics", timeout: Duration.seconds(8), interval: Duration.seconds(10), healthyThresholdCount: 2, unhealthyThresholdCount: 4, healthyHttpCodes: "200", }); // 本質ではないが、Gravition2 で動作させたいために RuntimePlatform のプロパティを上書きしている const fargateServiceTaskdef = fargateService.taskDefinition.node .defaultChild as ecs.CfnTaskDefinition; fargateServiceTaskdef.addPropertyOverride("RuntimePlatform", { CpuArchitecture: "ARM64", OperatingSystemFamily: "LINUX", }); // AMP への書き込み権限を付与する fargateService.taskDefinition.taskRole.addManagedPolicy( iam.ManagedPolicy.fromAwsManagedPolicyName( "AmazonPrometheusRemoteWriteAccess" ) ); // (2021/12/18) Amazon Managed Service for Prometheus のワークスペースを作成して、Prometheus の remote-write URL を取得する const apsWorkspace = new aps.CfnWorkspace( this, `${projectName}-prom-workspace`, { alias: `${projectName}-prom-workspace`, } ); const apsWorkspaceRemoteUrl = `${apsWorkspace.attrPrometheusEndpoint}api/v1/remote_write`; // (2021/12/18) 本記事で頻出する "エンドポイント - リモート書き込み URL" をコンソールに出力する new CfnOutput(this, "prom-remote-write-url", { value: apsWorkspaceRemoteUrl, description: "Prometheus Workspace の remote-write URL", exportName: "PromRemoteWriteURL", }); // AMP へメトリクス情報を送信するための Prometheus Agent コンテナを追加する const containerName = `${projectName}-prometheus-agent`; fargateService.taskDefinition.addContainer(containerName, { containerName, image: ecs.ContainerImage.fromAsset("prometheus-agent"), memoryReservationMiB: 128, environment: { // (2021/12/18) CDK 経由で作成した Prometheus の remote-write URL を設定する REMOTE_WRITE_URL: apsWorkspaceRemoteUrl, }, logging: new ecs.AwsLogDriver({ streamPrefix: `/${projectName}/prometheus-agent`, logRetention: logs.RetentionDays.ONE_DAY, }), }); // Grafana のタスク定義を作成する const grafanaDashboardTaskDefinition = new ecs.FargateTaskDefinition( this, `${projectName}-grafana-taskdef`, { family: `${projectName}-grafana-taskdef`, } ); // Grafana のタスクが Prometheus Query を叩けるように権限付与する grafanaDashboardTaskDefinition.taskRole.addManagedPolicy( iam.ManagedPolicy.fromAwsManagedPolicyName("AmazonPrometheusQueryAccess") ); // Grafana のコンテナを追加する。パスプレフィクスには dashboard を設定する const grafanaDashboardContainerName = `${projectName}-grafana-dashboard`; grafanaDashboardTaskDefinition.addContainer(grafanaDashboardContainerName, { containerName: grafanaDashboardContainerName, image: ecs.ContainerImage.fromRegistry("public.ecr.aws/ubuntu/grafana"), environment: { AWS_SDK_LOAD_CONFIG: "true", GF_AUTH_SIGV4_AUTH_ENABLED: "true", GF_SERVER_SERVE_FROM_SUB_PATH: "true", GF_SERVER_ROOT_URL: "%(protocol)s://%(domain)s/dashboard", }, portMappings: [{ containerPort: 3000 }], memoryLimitMiB: 512, logging: new ecs.AwsLogDriver({ streamPrefix: `/${projectName}/grafana-dashboard`, logRetention: logs.RetentionDays.ONE_DAY, }), }); const grafanaDashboardServiceName = `${projectName}-grafana-dashboard-service`; const grafanaDashboardService = new ecs.FargateService( this, grafanaDashboardServiceName, { serviceName: grafanaDashboardServiceName, cluster: fargateService.cluster, taskDefinition: grafanaDashboardTaskDefinition, desiredCount: 1, } ); // Grafana のタスクを ALB のターゲットグループに紐づける fargateService.listener.addTargets( `${projectName}-grafana-dashboard-target`, { priority: 1, conditions: [elbv2.ListenerCondition.pathPatterns(["/dashboard/*"])], healthCheck: { path: "/dashboard/login", interval: Duration.seconds(10), timeout: Duration.seconds(8), healthyThresholdCount: 2, unhealthyThresholdCount: 3, healthyHttpCodes: "200", }, port: 3000, protocol: elbv2.ApplicationProtocol.HTTP, targets: [grafanaDashboardService], } ); } } </code></pre> <p>その後、<code>cdk deploy</code> でインフラを構築します。</p> <p><img src="https://i.gyazo.com/c7da0f6c6b5a57edee47ae20a8026f8f.png" alt="CDK によるインフラ構築が正常に実行された時の様子" /><br /> <strong>CDK によるインフラ構築が正常に実行された時の様子</strong></p> <p>デプロイが正常に完了したのを確認したら、<code>Outputs</code> に出力されている <strong><code>PrometheusAgentTestStack.prometheusagenttestfargateserviceServiceURL<識別子></code> の URL 末尾に <code>/metrics</code> を付与してアクセスしてみます。</strong> 出力されている URL のフォーマットは <code>http://<識別子>.ap-northeast-1.elb.amazonaws.com</code> になります。</p> <p>つまり、<strong><code>http://<識別子>.ap-northeast-1.elb.amazonaws.com/metrics</code> にアクセスします。</strong></p> <p><img src="https://i.gyazo.com/c13a2b0efc3bb96e79b4f1f5a2886a8a.png" alt="ALB 経由で Node.js アプリにアクセス可能なことを確認する" /><br /> <strong>ALB 経由で Node.js アプリにアクセス可能なことを確認する</strong></p> <p>また、<code>Outputs</code> に出力されている <strong><code>PrometheusAgentTestStack.promremotewriteurl</code> は後に利用する <code>エンドポイント - リモート書き込み URL</code> で使用するので控えておきます。</strong></p> <p>ここまでで AWS CDK でのインフラ構築作業は完了しました。最後に Grafana で AMP のメトリクスを可視化するための作業を進めていきます。</p> <h1 id="Grafana で Prometheus (AMP) のメトリクスを可視化する"><a href="#Grafana+%E3%81%A7+Prometheus+%28AMP%29+%E3%81%AE%E3%83%A1%E3%83%88%E3%83%AA%E3%82%AF%E3%82%B9%E3%82%92%E5%8F%AF%E8%A6%96%E5%8C%96%E3%81%99%E3%82%8B">Grafana で Prometheus (AMP) のメトリクスを可視化する</a></h1> <p>先ほどの <code>/metrics</code> パスへのアクセス同様、<code>Outputs</code> に出力されている URL の末尾に <code>/dashboard/login</code> を付与してアクセスします。<strong>Grafana の初期ユーザおよびパスワードは <code>admin</code> となります。</strong></p> <p>つまり、<code>http://<識別子>.ap-northeast-1.elb.amazonaws.com/dashboard/login</code> にアクセスしてみます。</p> <p><img src="https://i.gyazo.com/fe4ea6515f54dec23234e10a93023a36.png" alt="ログインを行う" /></p> <p>ログイン情報が正しければ、新しいパスワードを設定する画面に遷移するので新たなパスワードを入力してログインを終えます。ログイン後は、Prometheus (AMP) をデータソースとして追加するために下記の操作を行います。</p> <p><img src="https://i.gyazo.com/599d49e4167ccc35c5d44d5df7678036.png" alt="1. 歯車アイコンをクリックして <code>Data sources</code> をクリックする" /><br /> <strong>1. 歯車アイコンをクリックして <code>Data sources</code> をクリックする</strong></p> <p><img src="https://i.gyazo.com/e88bfc58a35deaa16a16920a5e551837.png" alt="2. <code>Add data source</code> ボタンをクリックする" /><br /> <strong>2. <code>Add data source</code> ボタンをクリックする</strong></p> <p><img src="https://i.gyazo.com/1c8031a3182f03e20194bf0b76e66e5a.png" alt="3. データソースとして Prometheus を選択する" /><br /> <strong>3. データソースとして Prometheus を選択する</strong></p> <p><img src="https://i.gyazo.com/1684c1fca9decb29e60bd654400fd06b.png" alt="4. Prometheus をデータソースとして追加する" /></p> <p><img src="https://i.gyazo.com/2119361eac350044e783ed0c9280580a.png" alt="4. Prometheus をデータソースとして追加する" /></p> <p><img src="https://i.gyazo.com/00cc7edbfc45dbd43ad3b0cccea72c7d.png" alt="4. Prometheus をデータソースとして追加する" /><br /> <strong>4. Prometheus をデータソースとして追加する</strong></p> <p>Prometheus (AMP) に送信したメトリクスを Grafana で可視化するための準備が整ったので、実際に Grafana のダッシュボードでメトリクスを可視化してみます。手っ取り早くメトリクスを可視化するため、ダッシュボードには <a target="_blank" rel="nofollow noopener" href="https://grafana.com/grafana/dashboards/11159">NodeJS Application Dashboard</a> を利用します。</p> <p><img src="https://i.gyazo.com/b36c0281e273e21fc9474666786281c3.png" alt="1. + アイコンをクリックして、<code>Import</code> をクリックする" /><br /> <strong>1. + アイコンをクリックして、<code>Import</code> をクリックする</strong></p> <p><img src="https://i.gyazo.com/130718dff8b86758acb415764bd37338.png" alt="2. <code>NodeJS Application Dashboard</code> の ID を入力して <code>Load</code> ボタンをクリックする" /><br /> <strong>2. <code>NodeJS Application Dashboard</code> の ID を入力して <code>Load</code> ボタンをクリックする</strong></p> <p><img src="https://i.gyazo.com/2aa8903f3f64d8021699cd5d4bfa9757.png" alt="3. 必要な情報を入力して <code>NodeJS Application Dashboard</code> のインポートを完了する" /><br /> <strong>3. 必要な情報を入力して <code>NodeJS Application Dashboard</code> のインポートを完了する</strong></p> <p><img src="https://i.gyazo.com/1378b09c281da28653917ee40494dee8.png" alt="4. ダッシュボードから Prometheus のメトリクスが確認できる" /><br /> <strong>4. ダッシュボードから Node.js アプリのメトリクスが確認できる</strong></p> <p>ここまでの手順でメトリクスの可視化は完了しましたが、負荷に応じて実際にメトリクスが変化する様子も確認してみます。<a target="_blank" rel="nofollow noopener" href="https://github.com/tsenart/vegeta">Vegeta</a> を利用して、実際に負荷をかけてみます。下記コマンドを実行します。</p> <pre><code class="bash">echo 'GET http://<識別子>.ap-northeast-1.elb.amazonaws.com/metrics' | vegeta attack -duration=5s | vegeta report </code></pre> <p>その後、再び Grafana のダッシュボードを見にいきます。負荷をかけた時間帯のみグラフに変化があることを確認できるはずです。</p> <p><img src="https://i.gyazo.com/d019d33d3e4bd321ae4d1f4bbecc6ef8.png" alt="ダッシュボードの CPU 使用率のグラフに変化があったことを確認できる" /><br /> <strong>ダッシュボードの CPU 使用率のグラフに変化があったことを確認できる</strong></p> <h1 id="おわりに"><a href="#%E3%81%8A%E3%82%8F%E3%82%8A%E3%81%AB">おわりに</a></h1> <p>今回は ECS Fargate のメトリクスを Prometheus Agent で Amazon Managed Service for Prometheus (AMP) に送信し、それを Grafana で可視化する方法について紹介しました。</p> <p>ECS のサービスでタスクを実行する場合は <a target="_blank" rel="nofollow noopener" href="https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/service-discovery.html">サービスディスカバリ</a> の利用が可能なため、Prometheus の <a target="_blank" rel="nofollow noopener" href="https://prometheus.io/docs/prometheus/latest/configuration/configuration/#dns_sd_config">サービスディスカバリの設定</a> を行うことで、単一の Prometheus で全てのコンテナのメトリクスを扱うことも可能です。</p> <p>また Node.js アプリを作成する際に利用した <code>prom-client</code> で <a target="_blank" rel="nofollow noopener" href="https://github.com/siimon/prom-client#custom-metrics">カスタムメトリクス</a> を作成することで、監視したい項目を自由に増やすことも可能です。</p> <p>本記事が ECS Fargate を監視する際の検討材料の 1 つとなれたら幸いです。</p> <h1 id="参考リンク"><a href="#%E5%8F%82%E8%80%83%E3%83%AA%E3%83%B3%E3%82%AF">参考リンク</a></h1> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://aws.amazon.com/jp/fargate/">AWS Fargate(サーバーやクラスターの管理が不要なコンテナの使用)| AWS</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://prometheus.io/blog/2021/11/16/agent/">Introducing Prometheus Agent Mode, an Efficient and Cloud-Native Way for Metric Forwarding | Prometheus</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://aws.amazon.com/jp/prometheus/">Amazon Managed Service for Prometheus | フルマネージド Prometheus | Amazon Web Services</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://grafana.com/">Grafana: The open observability platform | Grafana Labs</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://aws.amazon.com/jp/cdk/">AWS クラウド開発キット – アマゾン ウェブ サービス</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/siimon/prom-client">siimon/prom-client: Prometheus client for node.js</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/tsenart/vegeta">tsenart/vegeta: HTTP load testing tool and library. It's over 9000!</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://grafana.com/grafana/dashboards/11159">NodeJS Application Dashboard dashboard for Grafana | Grafana Labs</a></li> </ul> nikaera tag:crieit.net,2005:PublicArticle/17640 2021-09-08T23:30:59+09:00 2021-09-08T23:30:59+09:00 https://crieit.net/posts/refer-params-package-json-in-script-20210908 package.json の情報をスクリプト内で参照したい <p>package.json の情報をスクリプト内で参照したくなったのでその方法をメモ。</p> <h2 id="方法"><a href="#%E6%96%B9%E6%B3%95">方法</a></h2> <pre><code class="js">const packageJson = require('./package.json'); console.log(packageJson.version); // 0.0.1 console.log(packageJson.dependencies); // { bootstrap: '^5.1.0' } </code></pre> <p>普通に <code>require</code> で読み込めるようです。</p> <pre><code class="js">const packageJson = require('./package.json'); console.log(packageJson.version); // 0.0.1 console.log(packageJson.devDependencies['browser-sync']); // ^2.27.5 </code></pre> <p>パッケージ名にハイフンがある場合はオブジェクトのキー名を指定する形式で使用パッケージのバージョン情報が取得できますね。</p> <p>思ったよりも簡単にできることが確認できました。</p> <p>ちなみに……</p> <pre><code class="bash">> node -v v16.3.0 > npm -v 7.15.1 </code></pre> <p>実行環境はとあるプロジェクトでこんな感じでした。最新に上げないといけませんがそれはそれとして。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/k_bobchin/items/c3e43b944d436d857381">package.jsonのversionをコマンド(ワンライナー)で取り出したい - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://dev.to/origamium/package-json-1626">package.jsonの各種要素を読み込みたい! - DEV Community</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://docs.npmjs.com/cli/v7/using-npm/scripts#packagejson-vars">scripts | npm Docs</a> <ul> <li><code>process.env.npm_package_XXXXXXXXXX</code> の形は手元の環境で試したところ <code>undefined</code> になってしまいました……</li> </ul></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/17638 2021-09-07T23:46:08+09:00 2021-09-07T23:46:08+09:00 https://crieit.net/posts/npm-package-publish-20210907 npm でパッケージを公開する <p>一度は試してみたいと思っていた npmパッケージ の公開をやってみることにします。</p> <h2 id="パッケージの準備"><a href="#%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E3%81%AE%E6%BA%96%E5%82%99">パッケージの準備</a></h2> <p>今回はテストのため、事前に作ってあった<a href="https://crieit.net/posts/show-image-in-consolelog-20210320">console.log で画像を表示するプログラム</a>で試すことにしてみます。</p> <p>ちなみにパッケージは以下のものがあるようなイメージで。</p> <ul> <li><code>package.json</code> <ul> <li><code>name</code></li> <li><code>description</code></li> <li><code>version</code></li> <li><code>main</code></li> <li><code>author</code></li> <li><code>license</code></li> <li>辺りが記述されていること</li> </ul></li> <li><code>.gitignore</code></li> <li>(<code>.npmignore</code>)</li> <li><code>LICENSE</code></li> <li>テストコード</li> <li>CI設定</li> </ul> <h3 id="リポジトリ"><a href="#%E3%83%AA%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA">リポジトリ</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/arm-band/console-cafebabe">GitHub - arm-band/console-cafebabe: npmパッケージのテスト。 console.log で画像を出力します。</a></li> </ul> <p>一揃いを準備。ついでに <a href="https://crieit.net/posts/badge-of-github-actions-with-chrome-extension-20210106">Github Actions でバッジを付けておきます</a>。</p> <h2 id="npm アカウントの作成"><a href="#npm+%E3%82%A2%E3%82%AB%E3%82%A6%E3%83%B3%E3%83%88%E3%81%AE%E4%BD%9C%E6%88%90">npm アカウントの作成</a></h2> <p>続いて npm にアカウントを作成します。</p> <p><a href="https://crieit.now.sh/upload_images/9672fb71cb8e0333916557c2057655fa6137799cf1796.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/9672fb71cb8e0333916557c2057655fa6137799cf1796.jpg?mw=700" alt="Sign Up" /></a></p> <p>Sign Up。</p> <p><a href="https://crieit.now.sh/upload_images/3c75850fb6182f5c8e4d336a184321ea613779a254a2b.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/3c75850fb6182f5c8e4d336a184321ea613779a254a2b.jpg?mw=700" alt="必要事項の入力" /></a></p> <p>ユーザ名、パスワード、メールアドレスを入力。ちなみにメールアドレスはパッケージのメタ情報に含まれて第三者に公開される可能性がある、との注意書きがあるので念のため専用のメールアカウントを用意しておきます。</p> <p><a href="https://crieit.now.sh/upload_images/3bfd2188deb990ef3a473945295cd516613779a4c20dc.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/3bfd2188deb990ef3a473945295cd516613779a4c20dc.jpg?mw=700" alt="ログイン後、ページトップに認証を求める通知が" /></a></p> <p>ログインするとメールでの認証を求められるので先ほど入力したメールアドレスに届いている認証のボタンを押しておきます。</p> <p><a href="https://crieit.now.sh/upload_images/7aa08684abb1c658284d0f62b6d37812613779a633ac7.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/7aa08684abb1c658284d0f62b6d37812613779a633ac7.jpg?mw=700" alt="認証完了" /></a></p> <p>認証完了。</p> <h2 id="npmパッケージの公開"><a href="#npm%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E3%81%AE%E5%85%AC%E9%96%8B">npmパッケージの公開</a></h2> <p>npmアカウント の作成が完了したらいよいよパッケージの公開作業です。</p> <p>パッケージのプロジェクトのルートディレクトリでターミナルを開き、 <code>npm login</code> で npm にログインします。</p> <pre><code class="bash">> npm login npm notice Log in on https://registry.npmjs.org/ Username: YOURNPMUSERNAME Password: Email: (this IS public) [email protected] Logged in as YOURNPMUSERNAME on https://registry.npmjs.org/. </code></pre> <p>OK。次に念のためパッケージとして公開されるファイルを確認しておきます。</p> <pre><code class="bash">> npm pack npm notice npm notice 📦 [email protected] npm notice === Tarball Contents === npm notice 125B __tests__/main.test.js npm notice 401B .github/workflows/ci.yml npm notice 1.0kB LICENSE npm notice 44B bin/cli.js npm notice 36B bin/postinstall.js npm notice 82B index.js npm notice 57B jest.config.js npm notice 417B package.json npm notice 277B readme.md npm notice 6.9kB src/main.js npm notice === Tarball Details === npm notice name: console-cafebabe npm notice version: 0.0.1 npm notice filename: console-cafebabe-0.0.1.tgz npm notice package size: 6.7 kB npm notice unpacked size: 9.4 kB npm notice shasum: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx npm notice integrity: sha512-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx npm notice total files: 10 npm notice console-cafebabe-0.0.1.tgz </code></pre> <p><code>npm pack</code> するとtgz形式でパッケージの圧縮ファイルが生成されます。 <code>Tarball Contents</code> の中身や実際に展開して中身を確認し、公開したくないファイルが混ざってないか最後のチェックを行います。問題なければ公開へ。</p> <pre><code class="bash">> npm publish npm notice npm notice 📦 [email protected] npm notice === Tarball Contents === npm notice 125B __tests__/main.test.js npm notice 401B .github/workflows/ci.yml npm notice 1.0kB LICENSE npm notice 44B bin/cli.js npm notice 36B bin/postinstall.js npm notice 82B index.js npm notice 57B jest.config.js npm notice 417B package.json npm notice 277B readme.md npm notice 6.9kB src/main.js npm notice === Tarball Details === npm notice name: console-cafebabe npm notice version: 0.0.1 npm notice filename: console-cafebabe-0.0.1.tgz npm notice package size: 6.7 kB npm notice unpacked size: 9.4 kB npm notice shasum: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx npm notice integrity: sha512-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx npm notice total files: 10 npm notice + [email protected] </code></pre> <p><code>npm publish</code> で公開します。わりとあっという間に終わります。</p> <p><a href="https://crieit.now.sh/upload_images/9dc5e6e5eee184c74f284f6940e3cca7613779a830538.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/9dc5e6e5eee184c74f284f6940e3cca7613779a830538.jpg?mw=700" alt="npm で自分のパッケージを検索してみる" /></a></p> <p>npm 上で検索すると公開したパッケージが見付かります。きちんと公開されていますね!</p> <h2 id="インストールテスト"><a href="#%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%83%86%E3%82%B9%E3%83%88">インストールテスト</a></h2> <p>今度はインストールしてみましょう。 <code>package.json</code> で以下のように自分のパッケージを <code>dependencies</code> に追加します。</p> <pre><code class="json"> "dependencies": { "console-cafebabe": "0.0.1" }, </code></pre> <p><code>npm i -D</code> または <code>yarn</code> 。</p> <p><a href="https://crieit.now.sh/upload_images/0118b0fb25910ab65c58a451c379e8a8613779a9e751f.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/0118b0fb25910ab65c58a451c379e8a8613779a9e751f.jpg?mw=700" alt="node_modules の中に発見" /></a></p> <p><code>node_modules</code> の中に入ってきましたね。これだけでもちょっと感動。</p> <pre><code class="js">import consoleCafebabe from 'console-cafebabe'; consoleCafebabe.coffee(); </code></pre> <p>Webpack前提のプロジェクトのエントリポイントのJSに <code>import</code> して、想定通りの使い方としてメソッドを呼び出してみます。</p> <p><a href="https://crieit.now.sh/upload_images/e733769175cd251259e35b3af3b45d1d613779abdeefa.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e733769175cd251259e35b3af3b45d1d613779abdeefa.jpg?mw=700" alt="console.log に画像などが表示された" /></a></p> <p>作った通り、 <code>console.log</code> に画像や装飾された文字列が表示されました。成功です。</p> <h2 id="ログアウト"><a href="#%E3%83%AD%E3%82%B0%E3%82%A2%E3%82%A6%E3%83%88">ログアウト</a></h2> <pre><code class="bash">> npm logout > </code></pre> <p>パッケージ公開後は <code>npm logout</code> でターミナル上でもログアウトしておくと事故の確率が減らせそうなのでログアウトしておきます。</p> <p>これで一通りの流れは押さえることができたと思います。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/TsutomuNakamura/items/f943e0490d509f128ae2">初めてのnpm パッケージ公開 - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/hoshimado/items/c6f1484297d974f44f19">npmでパッケージを公開してみた手順の記録 - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/fnobi/items/f6b1574fb9f4518ed520">3分でできるnpmモジュール - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://chaika.hatenablog.com/entry/2019/08/15/000000">初めての npm パッケージ公開したメモ - かもメモ</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/righteous/items/e5448cb2e7e11ab7d477#bin">【初心者向け】NPMとpackage.jsonを概念的に理解する - Qiita</a></li> </ul> <h3 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://bagelee.com/programming/javascript-2/patch-package/">かゆいところに手が届く!patch-packageでnpmパッケージを乗りこなそう - bagelee(ベーグリー)</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/sadnessOjisan/items/3069e79038c961458ba2">思いもよらないものをnpm publishしてしまった話(前任者の顔写真など) - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://efcl.info/2018/06/21/can-npm-publish/">npm publishできるかを判定するコマンドラインツール: can-npm-publish | Web Scratch</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/17604 2021-08-17T23:17:25+09:00 2021-08-17T23:17:25+09:00 https://crieit.net/posts/gulp-imagemin-become-pure-esm-package-20210817 gulp-imagemin で Error [ERR_REQUIRE_ESM]: Must use import to load ES Module と怒られる <h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2> <p>ある日、 package gardening をして Gulp を走らせたら</p> <blockquote> <p>Error [ERR_REQUIRE_ESM]: Must use import to load ES Module</p> </blockquote> <p>と怒られてしまいました。</p> <pre><code class="bash">$ gulp Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: PATH\TO\PROJECT\DIRECTORY\node_modules\gulp-imagemin\index.js require() of ES modules is not supported. require() of PATH\TO\PROJECT\DIRECTORY\node_modules\gulp-imagemin\index.js from PATH\TO\PROJECT\DIRECTORY\gulp\tasks\imagemin.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules. Instead rename index.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from PATH\TO\PROJECT\DIRECTORY\node_modules\gulp-imagemin\package.json. </code></pre> <p>今回は <code>gulp-imagemin</code> のスクリプトの中に起因するようなので、そちらに手を入れるのは避けたいところです。</p> <p>さて、どうしたものか。</p> <h2 id="結論"><a href="#%E7%B5%90%E8%AB%96">結論</a></h2> <p>結論から先にいうと、 <code>gulp-imagemin</code> をダウングレードすることで回避するしかなさそうです。</p> <p>今回は <code>8.0.0</code> から既存の <code>7.1.0</code> にダウングレードします。</p> <h2 id="調査"><a href="#%E8%AA%BF%E6%9F%BB">調査</a></h2> <h3 id="&quot;type&quot;: &quot;module&quot;"><a href="#%26quot%3Btype%26quot%3B%3A+%26quot%3Bmodule%26quot%3B">"type": "module"</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="http://var.blog.jp/archives/80335431.html">Node.js の ESModules</a></li> </ul> <p>検索すると、 <code>package.json</code> にパラメータを追記する方法が出てきます。</p> <pre><code class="json">{ "engines": { "node": ">=16.0.0", "npm": ">=6.14.9" }, "engineStrict": true, "prettier": {}, "type": "module" // 追記 } </code></pre> <p><code>"type": "module"</code> を追記する方法です。</p> <p>しかし、この方法を採用する場合、既存の Gulpタスク 全てを CommonJS (<code>require</code>) から ES Modules (<code>import</code>) に書き換える必要があります。</p> <p>それはそれで既存スクリプトの書き換えが大変なことになってしまうので、この方法は今回は不採用。</p> <h3 id="公式リポジトリを覗く"><a href="#%E5%85%AC%E5%BC%8F%E3%83%AA%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA%E3%82%92%E8%A6%97%E3%81%8F">公式リポジトリを覗く</a></h3> <p>そこで公式リポジトリを覗いてみます。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/sindresorhus/gulp-imagemin/issues/356">I have a question · Issue #356 · sindresorhus/gulp-imagemin · GitHub</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/sindresorhus/gulp-imagemin/issues/355">8.0.0 - ESM only? Serious? · Issue #355 · sindresorhus/gulp-imagemin · GitHub</a></li> </ul> <p>早速同じ内容の Issues が散見されます。そして回答では「リリースノートを見よ」とのこと。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/sindresorhus/gulp-imagemin/releases">Releases · sindresorhus/gulp-imagemin · GitHub</a></li> </ul> <blockquote> <p>This package is now pure ESM. Please <a target="_blank" rel="nofollow noopener" href="https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c">read this</a>.</p> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/sindresorhus/gulp-imagemin/releases">Releases · sindresorhus/gulp-imagemin · GitHub</a></p> </blockquote> <p>最初からここを見ましょう、という話ではあるのですが。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c">Pure ESM package · GitHub</a></li> </ul> <p>ご覧の通り、「<code>8.0.0</code> からは ES Modules のパッケージになっていますよー」というアナウンス。</p> <p>したがって、先の結論の通り CommonJS を使用したければダウングレードせよ、ということになります。是非もなし。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="http://var.blog.jp/archives/80335431.html">Node.js の ESModules</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://zenn.dev/tomon9086/scraps/3a1d9d3eed4864">ESMで書かれたライブラリをNextで使うと[ERR_REQUIRE_ESM]で落ちる件について</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/sindresorhus/gulp-imagemin/issues/356">I have a question · Issue #356 · sindresorhus/gulp-imagemin · GitHub</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/sindresorhus/gulp-imagemin/issues/355">8.0.0 - ESM only? Serious? · Issue #355 · sindresorhus/gulp-imagemin · GitHub</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/sindresorhus/gulp-imagemin/releases">Releases · sindresorhus/gulp-imagemin · GitHub</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c">Pure ESM package · GitHub</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/17580 2021-08-09T17:46:21+09:00 2021-08-09T17:46:51+09:00 https://crieit.net/posts/bye-nodist-hello-volta-20210809 Node.js のバージョン管理として Volta を使う <p>Nodist から脱却する方法第二弾。</p> <h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2> <p><a href="https://crieit.net/posts/build-enviroment-node-version-switch-by-docker-20210706">Docker で Node.js のバージョン切替環境を構築する</a>で一度 Docker 環境上に Node.js 環境を作りましたが、メモリ領域の不足で遅かったり Browsersync との連動が面倒だったりしたので、やはり一枚レイヤーを挟まずホスト側でストレートに構築する方法も模索すべきか……と思い至ったので Volta を試すことにしました。</p> <h2 id="Nodist のアンインストール"><a href="#Nodist+%E3%81%AE%E3%82%A2%E3%83%B3%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB">Nodist のアンインストール</a></h2> <p>今回はPCに直接インストールする形なのでモロに Nodist のバッティングします。そこで、まずは Nodist をアンインストールします。</p> <h3 id="1. Nodist のアンインストール"><a href="#1.+Nodist+%E3%81%AE%E3%82%A2%E3%83%B3%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB">1. Nodist のアンインストール</a></h3> <p>まずはアンインストール。コンパネからでも Nodist のインストールフォルダにある <code>uninstall.exe</code> を実行するのでもどちらでも。</p> <p><a href="https://crieit.now.sh/upload_images/4001730b70c2c2a0220da069b81e7e2f6110eb10f0b73.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/4001730b70c2c2a0220da069b81e7e2f6110eb10f0b73.jpg?mw=700" alt="Nodist の uninstall.exe" /></a></p> <h3 id="2. フォルダの削除"><a href="#2.+%E3%83%95%E3%82%A9%E3%83%AB%E3%83%80%E3%81%AE%E5%89%8A%E9%99%A4">2. フォルダの削除</a></h3> <p><a href="https://crieit.now.sh/upload_images/317374d2a921262dbb356bf0b5704a776110eb19bea1e.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/317374d2a921262dbb356bf0b5704a776110eb19bea1e.jpg?mw=700" alt="Nodist の残骸のフォルダやファイル" /></a></p> <p>アンインストールしても Nodist のフォルダやら残骸がインストールフォルダ (デフォルトならば <code>C:\Program Files (x86)\Nodist\</code>) に残るので、これを強制削除。</p> <h3 id="3. .npmrc の削除"><a href="#3.+.npmrc+%E3%81%AE%E5%89%8A%E9%99%A4">3. .npmrc の削除</a></h3> <p>使用ユーザのフォルダ直下に <code>.npmrc</code> (<code>C:\Users\<USERNAME>\.npmrc</code>) が残るのでこれを削除……と言いたいところですが、私の場合は存在していませんでした。</p> <p>そのため、ここはパス。</p> <h3 id="4. npm-cache の削除"><a href="#4.+npm-cache+%E3%81%AE%E5%89%8A%E9%99%A4">4. npm-cache の削除</a></h3> <p><a href="https://crieit.now.sh/upload_images/930f41ed08e28a2b6c613b2cb078b7516110eb22e4ef6.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/930f41ed08e28a2b6c613b2cb078b7516110eb22e4ef6.jpg?mw=700" alt="npm-cache の削除" /></a></p> <p>使用ユーザの<code>AppData\Roaming</code> の下に <code>npm-cache</code> (<code>C:\Users\<USERNAME>\AppData\Roaming\npm-cache</code>) が残るのでこれを削除。</p> <h3 id="5. 環境変数の確認"><a href="#5.+%E7%92%B0%E5%A2%83%E5%A4%89%E6%95%B0%E3%81%AE%E7%A2%BA%E8%AA%8D">5. 環境変数の確認</a></h3> <p><a href="https://crieit.now.sh/upload_images/d458bc9076579814d02a72128e8c8ff36110eb2c1b3d2.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/d458bc9076579814d02a72128e8c8ff36110eb2c1b3d2.jpg?mw=700" alt="環境変数" /></a></p> <p>環境変数に Nodist の Node.js へのパスが残っている場合はこれを削除……と言いたいところですが、私の場合は存在していませんでした。</p> <p>そのため、ここもパス。</p> <h2 id="Volta のインストール"><a href="#Volta+%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB">Volta のインストール</a></h2> <p>Nodist を削除したら、 Volta をインストールします。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/volta-cli/volta/releases">Releases · volta-cli/volta · GitHub</a></li> </ul> <p><a href="https://crieit.now.sh/upload_images/9f40ac9ec460dcb4945615229543b7306110eb3533ae0.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/9f40ac9ec460dcb4945615229543b7306110eb3533ae0.jpg?mw=700" alt="Volta のインストーラ" /></a></p> <p>記事執筆時は <code>1.0.4</code> が最新でした。この <code>msi</code> をダウンロード。後は実行してインストール。</p> <pre><code class="bash">>volta -v 1.0.4 </code></pre> <p>入りました。</p> <pre><code class="bash">>volta install [email protected] success: installed and set [email protected] (with [email protected]) as default 9 >node -v v16.3.0 >npm -v 7.15.1 </code></pre> <p>続いて適当なバージョンの Node.js をインストールします。 npm も入りました。良い感じですね。</p> <pre><code class="bash">>volta install yarn success: installed and set [email protected] as default 0 >yarn -v 1.22.11 </code></pre> <p>yarn も入れます。</p> <p>とあるプロジェクトで、 Node.js のバージョンを14系で固定してみます。</p> <pre><code class="bash">> volta pin node@14 success: pinned [email protected] (with [email protected]) in package.json </code></pre> <p>OK.</p> <pre><code class="json"> "volta": { "node": "14.17.4" } </code></pre> <p><code>package.json</code> に Volta 使用時の Node.js のバージョンが追記されました。これでプロジェクトごとにバージョンを切り替えられるのは良いですね。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <h3 id="Nodist アンインストール"><a href="#Nodist+%E3%82%A2%E3%83%B3%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB">Nodist アンインストール</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://zenn.dev/ymasaoka/articles/note-uninstall-nodish-windows">2020 年ではもう使えない Nodist はアンインストールする (Windows)</a></li> </ul> <h3 id="Volta"><a href="#Volta">Volta</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://zenn.dev/naoyukik/articles/1b861167479d6adfa32f">Nodeのバージョン管理ツールVOLTA⚡</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://dev.classmethod.jp/articles/node-version-volta/">Node.js のバージョン管理ツール Volta を使ってみる | DevelopersIO</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://zenn.dev/taichifukumoto/articles/how-to-use-volta">Node.jsのバージョン管理にVoltaを推したい</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/17486 2021-07-05T23:53:53+09:00 2021-07-05T23:53:53+09:00 https://crieit.net/posts/build-enviroment-node-version-switch-by-docker-20210706 Docker で Node.js のバージョン切替環境を構築する <h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2> <p>Windows環境では長らく <a target="_blank" rel="nofollow noopener" href="https://github.com/nullivex/nodist">Nodist</a> を使ってきましたが、流石にそろそろ見切りを付けて安定的に Node.js を使用できる環境を構築したいと考えました。</p> <p>理由としては以下の2点。</p> <ul> <li>そもそも Nodist のバージョンが <code>0.9.1</code> (<code>< 1.0.0</code>) であること</li> <li><code>0.9.1</code> のリリースが2019/3/30で、2年以上も更新がされていないこと</li> </ul> <p>正直、<a target="_blank" rel="nofollow noopener" href="https://zenn.dev/ymasaoka/articles/note-uninstall-nodish-windows">2020 年ではもう使えない Nodist はアンインストールする (Windows)</a>に全て書かれていることなので改めて書く必要はないかもしれません。</p> <p>個人的には <code>npm</code> の切替時にエラーになってしまって切り替えられないなどの不具合が発生し始めたので、先述したようにそろそろ限界かな、と感じるようになりました。</p> <p>そこで次の環境について考えたとき、やはり自PCの環境を汚さずに済むので Docker で構築することにしました。</p> <h2 id="構成"><a href="#%E6%A7%8B%E6%88%90">構成</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/arm-band/docker_sink_vertex">GitHub - arm-band/docker_sink_vertex: A collection of settings for running Node.js on Docker.</a></li> </ul> <h3 id="ディレクトリ構造"><a href="#%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E6%A7%8B%E9%80%A0">ディレクトリ構造</a></h3> <pre><code>PROJECT_ROOT/ │ ├ .env ├ .gitignore ├ docker-compose.yml └ readme.md </code></pre> <p>今回は Docker公式イメージ の <a target="_blank" rel="nofollow noopener" href="https://hub.docker.com/_/node">Node.js</a> を使うので、構成はいたってシンプルです。</p> <h3 id=".env"><a href="#.env">.env</a></h3> <pre><code class="env">NODE_VERSION=16.3.0 PROJECT_ROOT_DIRECTORY=../workspace/ </code></pre> <p>Docker Compose の前に <code>.env</code> のサンプルを。指定しているのは Node.js のイメージのバージョン指定とマウントするボリュームのパスです。</p> <p>今回はそもそもバージョン切替可能な Node.js の環境を構築することが主眼なので、バージョン指定をパラメータで指定できるようにしておきます。</p> <p>それから、 Node.js 環境で実行したいプロジェクトがあるはずなので、そのプロジェクトのディレクトリをコンテナ内にマウントさせるためにパス指定を記述しておきます。</p> <h3 id="docker-compose.yml"><a href="#docker-compose.yml">docker-compose.yml</a></h3> <pre><code class="yml">version: '3.8' services: vertex: image: "node:$NODE_VERSION" user: "node" volumes: # project root - $PROJECT_ROOT_DIRECTORY:/home/node/app tty: true ports: - "3000:3000" - "3001:3001" </code></pre> <p>以上を踏まえて Docker Compose の yml 。</p> <p><code>.env</code> で説明した通り、 Node.js のイメージ指定とボリュームマウントの指定があります。</p> <p>地味にポイントなのは <code>ports</code> 。今回は Browsersync を使った Gulpタスク を走らせようとしているのですが、ブラウザはホスト側にしかないため、ホストからコンテナ内の Browsersync の API を参照できるようにポートを指定しておきます。</p> <p>これで試験してみます。</p> <h2 id="試験"><a href="#%E8%A9%A6%E9%A8%93">試験</a></h2> <p>上述の <code>docker-compose.yml</code> を作る途中で <code>ports</code> 指定をせず、何もカスタマイズしていない Gulp を走らせました。すると、以下のようなエラーが発生しました。</p> <pre><code class="bash">Couldn't open browser (if you are using BrowserSync in a headless environment, you might want to set the open option to false) </code></pre> <p>上述した通りブラウザはホスト側にしかなく、一方で Browsersync 含む Gulpタスク はコンテナ内で動いているので、当然と言えば当然ですよね。</p> <p>これで <code>ports</code> 指定を加えました。</p> <p>また、 Browsersync 側の設定も変更を加えます。</p> <pre><code class="javascript">browserSync.init({ server: { baseDir: './dist/' }, // open: 'external', open: false, // ブラウザオープンをしない https: true }); </code></pre> <p><code>open</code>オプション を <code>false</code> にすることで、ブラウザを自動的に起動しないようにしました。これをしておかないと上述のエラーが発生してしまうので。</p> <p>代わりに手動でブラウザを開く必要がありますが、已む無し。 <code>https://localhost:3000/</code> できちんと見られるので良しとします。</p> <h2 id="試験2"><a href="#%E8%A9%A6%E9%A8%932">試験2</a></h2> <p>追試験を実施している最中に、一部の環境で <code>gulp.watch</code> による自動ビルドが2回に1回しか走らない現象に遭遇しました。</p> <p>Node.js のバージョンを切り替えてもダメで、しかも <code>ejs</code>ファイル はOKですが <code>scss</code>ファイル や <code>yml</code>ファイル はダメなど、条件がいまいちよく分かりません。</p> <p>検索してみるとオプションで <code>{ usePolling: true }</code> を指定すると直ったというのを見かけたので Gulpタスク に書き足してみました。</p> <p>すると、現象が収まりました……ということは、ファイルをウォッチするタイミングか何かが上手くいっていないということ?</p> <p>とりあえず動いたので良しとします。</p> <h2 id="試験3"><a href="#%E8%A9%A6%E9%A8%933">試験3</a></h2> <p>試験2の途中で Node.js のバージョンを切り替えたらそもそも <code>yarn</code> がエラーでコケるなど副作用が発生したので<a target="_blank" rel="nofollow noopener" href="https://labor.ewigleere.net/2021/07/05/node-sass-adjust-node16/">gulp-sass を Node.js v16 環境に合わせて調整する</a>に書いたように諸々対処しました。</p> <p>これでようやく想定している Gulpタスク 一式が使えるようになりました。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li>きっかけ <ul> <li><a target="_blank" rel="nofollow noopener" href="https://zenn.dev/ymasaoka/articles/note-uninstall-nodish-windows">2020 年ではもう使えない Nodist はアンインストールする (Windows)</a></li> </ul></li> <li><code>Couldn't open browser (if you are using BrowserSync in a headless environment, you might want to set the open option to false)</code> のエラー <ul> <li><a target="_blank" rel="nofollow noopener" href="https://stackoverflow.com/questions/42456424/browsersync-within-a-docker-container">javascript - Browsersync within a Docker container - Stack Overflow</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://kore1server.com/373/Laravel5.5%E3%80%81Mix%E3%81%AEBrowserSync">Laravel5.5、MixのBrowserSync</a></li> </ul></li> <li><code>gulp.watch</code> が動かない <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/AyumuNekozuki/niconico-darkmode/issues/54">【開発環境】gulpのwatchが動かない · Issue #54 · AyumuNekozuki/niconico-darkmode · GitHub</a></li> </ul></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/17485 2021-07-05T20:32:23+09:00 2021-07-05T20:32:23+09:00 https://crieit.net/posts/node-sass-adjust-node16-20210705 gulp-sass を Node.js v16 環境に合わせて調整する <p>Node.js の 16系 を試そうとしたら gulp-sass, node-sass 周りでエラーが発生したので対処をまとめておきます。</p> <h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2> <p>まず前提として</p> <ul> <li>Dart Sass で <code>/</code> を使った徐算が非推奨となった (代わりに <code>math.div</code> を使えとのこと) <ul> <li>参考: <a target="_blank" rel="nofollow noopener" href="https://labor.ewigleere.net/2021/06/07/dart-sass-warning-slash-as-division/">Dart Sass で DEPRECATION WARNING: Using / for division is deprecated and will be removed in Dart Sass 2.0.0. の警告が表示される - Ewig Leere(Lab2)</a></li> </ul></li> <li>上述現象を回避するため、 <code>sass</code> のバージョンを <code>1.32.12</code> で固定していた</li> </ul> <p>という事情があります。</p> <h2 id="現象"><a href="#%E7%8F%BE%E8%B1%A1">現象</a></h2> <p>この状態で Node.js のバージョンを 16系 に切り替えて <code>yarn</code> 。</p> <pre><code class="bash">error /home/node/app/node_modules/node-sass: Command failed. Exit code: 1 Command: node scripts/build.js Arguments: Directory: /home/node/app/node_modules/node-sass Output: Building: /usr/local/bin/node /home/node/app/node_modules/node-gyp/bin/node-gyp.js rebuild --verbose --libsass_ext= --libsass_cflags= --libsass_ldflags= --libsass_library= # 略 In file included from /home/node/.node-gyp/16.3.0/include/node/v8.h:30, from /home/node/.node-gyp/16.3.0/include/node/node.h:63, from ../../nan/nan.h:56, from ../src/binding.cpp:1: /home/node/.node-gyp/16.3.0/include/node/v8-internal.h: In function 'void v8::internal::PerformCastCheck(T*)': /home/node/.node-gyp/16.3.0/include/node/v8-internal.h:452:38: error: 'remove_cv_t' is not a member of 'std' !std::is_same<Data, std::remove_cv_t<T>>::value>::Perform(data); ^~~~~~~~~<del> /home/node/.node-gyp/16.3.0/include/node/v8-internal.h:452:38: note: suggested alternative: 'remove_cv' !std::is_same<Data, std::remove_cv_t<T>>::value>::Perform(data); ^</del>~~~~~~<del> remove_cv /home/node/.node-gyp/16.3.0/include/node/v8-internal.h:452:38: error: 'remove_cv_t' is not a member of 'std' /home/node/.node-gyp/16.3.0/include/node/v8-internal.h:452:38: note: suggested alternative: 'remove_cv' !std::is_same<Data, std::remove_cv_t<T>>::value>::Perform(data); ^</del>~~~~~~<del> remove_cv /home/node/.node-gyp/16.3.0/include/node/v8-internal.h:452:50: error: template argument 2 is invalid !std::is_same<Data, std::remove_cv_t<T>>::value>::Perform(data); ^ /home/node/.node-gyp/16.3.0/include/node/v8-internal.h:452:63: error: '::Perform' has not been declared !std::is_same<Data, std::remove_cv_t<T>>::value>::Perform(data); ^</del>~~<del> /home/node/.node-gyp/16.3.0/include/node/v8-internal.h:452:63: note: suggested alternative: 'herror' !std::is_same<Data, std::remove_cv_t<T>>::value>::Perform(data); ^</del>~~<del> herror ../src/binding.cpp: In function 'Nan::NAN_METHOD_RETURN_TYPE render(Nan::NAN_METHOD_ARGS_TYPE)': ../src/binding.cpp:284:98: warning: cast between incompatible function types from 'void (*)(uv_work_t*)' {aka 'void (*)(uv_work_s*)'} to 'uv_after_work_cb' {aka 'void (*)(uv_work_s*, int)'} [-Wcast-function-type] int status = uv_queue_work(uv_default_loop(), &ctx_w->request, compile_it, (uv_after_work_cb)MakeCallback); ^</del>~~~~~~~<del> ../src/binding.cpp: In function 'Nan::NAN_METHOD_RETURN_TYPE render_file(Nan::NAN_METHOD_ARGS_TYPE)': ../src/binding.cpp:320:98: warning: cast between incompatible function types from 'void (*)(uv_work_t*)' {aka 'void (*)(uv_work_s*)'} to 'uv_after_work_cb' {aka 'void (*)(uv_work_s*, int)'} [-Wcast-function-type] int status = uv_queue_work(uv_default_loop(), &ctx_w->request, compile_it, (uv_after_work_cb)MakeCallback); ^</del>~~~~~~~<del> In file included from ../../nan/nan.h:56, from ../src/binding.cpp:1: ../src/binding.cpp: At global scope: /home/node/.node-gyp/16.3.0/include/node/node.h:806:43: warning: cast between incompatible function types from 'void (*)(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE)' {aka 'void (*)(v8::Local<v8::Object>)'} to 'node::addon_register_func' {aka 'void (*)(v8::Local<v8::Object>, v8::Local<v8::Value>, void*)'} [-Wcast-function-type] (node::addon_register_func) (regfunc), \ ^ /home/node/.node-gyp/16.3.0/include/node/node.h:840:3: note: in expansion of macro 'NODE_MODULE_X' NODE_MODULE_X(modname, regfunc, NULL, 0) // NOLINT (readability/null_usage) ^</del>~~~~~~~~<del> ../src/binding.cpp:358:1: note: in expansion of macro 'NODE_MODULE' NODE_MODULE(binding, RegisterModule); ^</del>~~~~~~~~ make: *** [binding.target.mk:133: Release/obj.target/binding/src/binding.o] Error 1 make: Leaving directory '/home/node/app/node_modules/node-sass/build' gyp ERR! build error gyp ERR! stack Error: `make` failed with exit code: 2 gyp ERR! stack at ChildProcess.onExit (/home/node/app/node_modules/node-gyp/lib/build.js:262:23) gyp ERR! stack at ChildProcess.emit (node:events:394:28) gyp ERR! stack at Process.ChildProcess._handle.onexit (node:internal/child_process:290:12) gyp ERR! System Linux 5.10.25-linuxkit gyp ERR! command "/usr/local/bin/node" "/home/node/app/node_modules/node-gyp/bin/node-gyp.js" "rebuild" "--verbose" "--libsass_ext=" "--libsass_cflags=" "--libsass_ldflags=" "--libsass_library=" gyp ERR! cwd /home/node/app/node_modules/node-sass gyp ERR! node -v v16.3.0 </code></pre> <p>……滅茶苦茶怒られました (Docker内のLinux環境)。</p> <p>ちなみに Windows環境だと <code>yarn</code> 時にはエラーにはなりませんが、 Gulpタスク を走らせた際に同様のエラーメッセージが表示されます。</p> <p><a href="https://crieit.now.sh/upload_images/68677d73f1123631b69e1a1fa1061c7b60e2edbdef2fa.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/68677d73f1123631b69e1a1fa1061c7b60e2edbdef2fa.jpg?mw=700" alt="Windows環境でのエラーメッセージ" /></a></p> <h2 id="対処1 (sass, gulp-sass のバージョンを上げる)"><a href="#%E5%AF%BE%E5%87%A61+%28sass%2C+gulp-sass+%E3%81%AE%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3%E3%82%92%E4%B8%8A%E3%81%92%E3%82%8B%29">対処1 (sass, gulp-sass のバージョンを上げる)</a></h2> <p>そこでまずは <code>sass</code> のバージョンを上げます。</p> <p>ちなみに、 <code>1.35.0</code>以降 では <code>quietDeps: true</code> のフラグを追加することで、冒頭の警告を非表示にできるようになったとのことなので、そもそもの問題は回避できるはずです。</p> <pre><code class="json"> "devDependencies": { // 略 "gulp-sass": "^4.1.0", "sass": "1.35.1", // 1.32.12 からアップデート "fibers": "^5.0.0", "gulp-autoprefixer": "^7.0.1", // 略 } </code></pre> <p>これで <code>yarn</code> してみましたが、ダメ。</p> <p>ふと <code>gulp-sass</code> を見てみると、いつの間にか <code>5.0.0</code> がリリースされているではありませんか。</p> <pre><code class="json"> "devDependencies": { // 略 "gulp-sass": "^5.0.0", // 4.1.0 からアップデート "sass": "1.35.1", // 1.32.12 からアップデート "fibers": "^5.0.0", "gulp-autoprefixer": "^7.0.1", // 略 } </code></pre> <p>そこで、 <code>gulp-sass</code> も上げます。これで <code>yarn</code> すると正常に完了。</p> <p>安心して Gulpタスク を走らせると……</p> <pre><code class="bash">../src/coroutine.cc:134: void* find_thread_id_key(void*): Assertion `thread_id_key != 0x7777' failed. </code></pre> <p>…別のエラーで怒られました。</p> <h2 id="対処2 (fibers を削除)"><a href="#%E5%AF%BE%E5%87%A62+%28fibers+%E3%82%92%E5%89%8A%E9%99%A4%29">対処2 (fibers を削除)</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://stackoverflow.com/questions/67330252/thread-id-key-0x7777-function-find-thread-id-key-file-src-coroutine-cc">node.js - (thread_id_key != 0x7777), function find_thread_id_key, file ../src/coroutine.cc, line 134 - Stack Overflow</a></li> </ul> <p>検索すると上述のエラーは <code>fibers</code> が Node.js 16系 に対応していないために発生している模様。</p> <p>そのため、 <code>fibers</code> を削除します。</p> <pre><code class="json"> "devDependencies": { // 略 "gulp-sass": "^5.0.0", // 4.1.0 からアップデート "sass": "1.35.1", // 1.32.12 からアップデート "gulp-autoprefixer": "^7.0.1", // 略 } </code></pre> <p>併せてタスクも書き換えます。</p> <pre><code class="javascript">const sass = require('gulp-sass'); sass.compiler = require('sass'); //const Fiber = require('fibers'); // 略 .pipe(sass({ // fiber: Fiber, outputStyle: 'compressed', quietDeps: true // 警告回避のために追加 }).on('error', sass.logError)) // 略 </code></pre> <p><code>fibers</code> 関係を削除。</p> <p>これで <code>yarn</code> し直してタスクを再実行。</p> <pre><code class="bash">Error in plugin "gulp-sass" Message: gulp-sass 5 does not have a default Sass compiler; please set one yourself. Both the `sass` and `node-sass` packages are permitted. For example, in your gulpfile: var sass = require('gulp-sass')(require('sass')); </code></pre> <p>……別のエラーが発生しました。どうやら <code>gulp-sass</code> の Scssトランスパイラ の指定方法が変わったようです。</p> <h2 id="対処3 (gulp-sass のScssトランスパイラ指定方法を変更)"><a href="#%E5%AF%BE%E5%87%A63+%28gulp-sass+%E3%81%AEScss%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B9%E3%83%91%E3%82%A4%E3%83%A9%E6%8C%87%E5%AE%9A%E6%96%B9%E6%B3%95%E3%82%92%E5%A4%89%E6%9B%B4%29">対処3 (gulp-sass のScssトランスパイラ指定方法を変更)</a></h2> <pre><code class="javascript">const sass = require('gulp-sass')(require('sass')); //const Fiber = require('fibers'); // 略 .pipe(sass({ // fiber: Fiber, outputStyle: 'compressed', quietDeps: true // 警告回避のために追加 }).on('error', sass.logError)) // 略 </code></pre> <p>これでようやくタスクが正常に走るようになりました。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li>Dart Sass の警告回避: <ul> <li><a target="_blank" rel="nofollow noopener" href="https://sass-lang.com/documentation/js-api#quietdeps">Sass: JavaScript API</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/twbs/bootstrap/issues/34051">Sass deprecation warning: Using `/` for division is deprecated · Issue #34051 · twbs/bootstrap · GitHub</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/sass/dart-sass/issues/1333">support quietDeps flag · Issue #1333 · sass/dart-sass · GitHub</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/sass/dart-sass/issues/672">Add a --quiet-upstream flag · Issue #672 · sass/dart-sass · GitHub</a></li> </ul></li> <li>fibers: <a target="_blank" rel="nofollow noopener" href="https://stackoverflow.com/questions/67330252/thread-id-key-0x7777-function-find-thread-id-key-file-src-coroutine-cc">node.js - (thread_id_key != 0x7777), function find_thread_id_key, file ../src/coroutine.cc, line 134 - Stack Overflow</a></li> <li>gulp-sass: <a target="_blank" rel="nofollow noopener" href="https://www.npmjs.com/package/gulp-sass">gulp-sass - npm</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/17439 2021-06-25T03:57:06+09:00 2021-06-25T08:38:02+09:00 https://crieit.net/posts/puppeteer puppeteer でファイルをダウンロードするときに、任意のパスと名前で保存する <p><a target="_blank" rel="nofollow noopener" href="https://pptr.dev/">Puppeteer</a> を使ってファイルをダウンロードする際に、任意のパスと名前で保存したい。</p> <p>残念ながら、 現時点ではシンプルな方法は提供されていないようだ。<br /> 以下の Issue で何年にもわたって議論されているものの、 <strong>「コレ!」</strong> という解決方法は無さそう。<br /> <a target="_blank" rel="nofollow noopener" href="https://github.com/puppeteer/puppeteer/issues/299">Question: How do I get puppeteer to download a file? · Issue #299 · puppeteer/puppeteer</a></p> <p>しかし、 この Issue の <a target="_blank" rel="nofollow noopener" href="https://github.com/puppeteer/puppeteer/issues/299#issuecomment-668087154">#issuecomment-668087154</a> のコメントで、 なかなか泥臭い方法で実現するヒントが書かれていた。<br /> これを参考にして、任意のパスと名前でダウンロードファイルを保存してみよう。</p> <h2 id="実行方法"><a href="#%E5%AE%9F%E8%A1%8C%E6%96%B9%E6%B3%95">実行方法</a></h2> <p>あらかじめ、 <a target="_blank" rel="nofollow noopener" href="https://www.npmjs.com/package/puppeteer">puppeteer</a> の npm パッケージをローカルにインストールしておく。</p> <pre><code class="console">npm install puppeteer --save </code></pre> <p>その状態で、後述の .js ファイルを nodejs で実行すれば OK だ。</p> <pre><code class="console">node puppeteer-download-with-specify-name.js </code></pre> <h2 id="コードと解説"><a href="#%E3%82%B3%E3%83%BC%E3%83%89%E3%81%A8%E8%A7%A3%E8%AA%AC">コードと解説</a></h2> <gist src="https://gist.github.com/advanceboy/557a7690e1b584d11f38ff86434aef65.js"></gist> <p>何をしているのかというと、 GitHub 上の <a target="_blank" rel="nofollow noopener" href="https://www.npmjs.com/package/puppeteer">puppeteer</a> のソースコード ZIP ファイルをダウンロードする際に、 <a target="_blank" rel="nofollow noopener" href="https://chromedevtools.github.io/devtools-protocol/">Chrome DevTools Protocol</a> を直叩きして、 任意のパスとファイル名で保存している。</p> <p>具体的なポイントは、主に 以下の 2点。</p> <ul> <li><code>Page.setDownloadBehavior</code> メソッドで、 ファイルのダウンロードの許可とダウンロード先のディレクトリを指定</li> <li><code>Fetch.enable</code> メソッドと <code>Fetch.requestPaused</code> イベントで、 ファイルダウンロードのレスポンスに <code>Content-Disposition</code> HTTP ヘッダーを無理やりねじ込む</li> </ul> <p><a target="_blank" rel="nofollow noopener" href="https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Content-Disposition"><code>Content-Disposition</code> HTTP ヘッダー</a> のドキュメントに書かれている通り、 <code>attachment</code> と <code>filename</code> ディレクティブを指定することで、 ファイルが (ブラウザ内で表示されるのではなく)ダウンロードが必要であることと、 ダウンロード時のファイル名を指定することができる。</p> <p>但し、 <code>Page.setDownloadBehavior</code> メソッドは <a target="_blank" rel="nofollow noopener" href="https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-setDownloadBehavior">実験的で且つ非推奨</a> なので、 将来にわたってサポートが続くかどうかはわからない点は、注意だ。<br /> 少なくとも、 Chromium 92.0.4512.0 (r884014) では問題なく動いている。</p> <p>ちなみに、実行する Chromium はヘッドレスモードでもヘッドフルモードでもどちらでも意図通り動くはず。</p> <hr /> <p>この方法は Chrome DevTools Protocol に思いっきり依存しているので、 Selenium など他のブラウザ自動化ツールでは同一の方法が難しく (※)、 Puppeteer ならではの方法と言える。<br /> ※: Selenium 4.x のプレリリース版を使えば、 Chrome DevTools Protocol にアクセスできるようだが、 イベントハンドラを書くのが難しそう? ドキュメントがそろってないのでまだなんとも…</p> <p>スクレイピング中にファイルをダウンロードする場合などでは、保存先のパスと名前を指定できたほうが良い気がするのだが……<br /> 今後の puppeteer や Chrome DevTools Protocol の更新でもっと簡単に実現できるようになることを期待しよう。</p> advanceboy tag:crieit.net,2005:PublicArticle/16823 2021-04-07T14:52:38+09:00 2021-04-07T14:54:07+09:00 https://crieit.net/posts/Node-js-Prisma-PrismaClient-import Node.jsのPrismaでPrismaClientがimportできない場合 <p>Node.jsでPrismaは下記のようにimportする。</p> <pre><code class="javascript">import { PrismaClient } from '@prisma/client' </code></pre> <p>しかしPrismaClientの定義がありません、みたいなことを言われてimportできない。</p> <p>公式ドキュメントに書いてあるとおり、 <code>prisma generate</code> を実行することでnode_modulesの中に生成してくれるらしい。</p> <pre><code>npx prisma generate </code></pre> <p><a target="_blank" rel="nofollow noopener" href="https://www.prisma.io/docs/concepts/components/prisma-client#2-installation">https://www.prisma.io/docs/concepts/components/prisma-client#2-installation</a></p> <p>それでもずっとエラーになってる、という場合はエディタを再起動しよう。</p> だら@Crieit開発者 tag:crieit.net,2005:PublicArticle/16414 2020-12-23T23:56:50+09:00 2020-12-24T00:00:19+09:00 https://crieit.net/posts/node-script-triggered-cron-20201223 cron から node スクリプトをキックする <p>cron から nodeスクリプト をキックする方法について長らく嵌まっていたのでメモ。</p> <h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2> <p>とあるサーバで <code>markdown-pdf</code> によるPDF生成を行いたくて、自前の nodeスクリプト を作成・運用していました。</p> <p>ただし、そのサーバは一定期間ごとに再起動をかけているため、その度に自前の nodeスプリプト のプロセスが切れてしまいます。そこで cron で「再起動後に nodeスクリプト をキックする」ようなジョブを書いたのですが……。</p> <pre><code class="bash">@reboot root node /PATH/TO/NODE-SCRIPT/index.js & 30 2 * * 0 root reboot </code></pre> <p>しかし、上手く行きませんでした。</p> <h2 id="調査・対処"><a href="#%E8%AA%BF%E6%9F%BB%E3%83%BB%E5%AF%BE%E5%87%A6">調査・対処</a></h2> <p><code>/var/log/cron</code> で確認すると、 cronジョブ 自体は実行されていそうなのですが、 <code>ps aux | grep "node"</code> で確認すると、 node のプロセスはありません。起動できていないようです。</p> <p>その原因が分からず悩まされていたのですが、ふと node のパスがおかしいのでは、と気付き以下のように修正。</p> <pre><code class="bash">@reboot root /usr/local/bin/node /PATH/TO/NODE-SCRIPT/index.js & 30 2 * * 0 root reboot </code></pre> <p>※ちなみにこのサーバは <code>n</code> を使って node がインストールされています。</p> <p>これで試験したところ、再起動後にスクリプトが起動・動作していることを確認できました。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://tomokazu-kozuma.com/periodically-run-a-script-with-cron/">crontabでスクリプトを定期実行する - ブロックチェーンエンジニアとして生きる</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/16378 2020-12-16T23:42:55+09:00 2020-12-20T00:38:39+09:00 https://crieit.net/posts/backstop-trial-and-error-20201215 BackstopJS で背景画像の高さを `vh` 単位で指定したページで画像やスクリーンショットが引き伸ばされる現象についてメモ (未解決) <p>メインビジュアル等で背景画像の高さを vh 単位で指定したページで、画像やスクリーンショットが引き伸ばされる現象に遭遇してしまいました。正常なスクリーンショットが撮影できないので困った……ということで、その現象について調べたメモです。</p> <h2 id="調査"><a href="#%E8%AA%BF%E6%9F%BB">調査</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/garris/BackstopJS/issues/785">Issues using vh units in CSS ・ Issue #785 ・ garris/BackstopJS</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/garris/BackstopJS/issues/820">Take screenshot without resizing page ・ Issue #820 ・ garris/BackstopJS</a></li> </ul> <p>調べたらそれと思しき現象の Issues が上がっていました。ただ、どちらも現在進行形でオープンになっているので解決には至っていない模様……。</p> <h2 id="検証"><a href="#%E6%A4%9C%E8%A8%BC">検証</a></h2> <p>スレッドの中で提示された対処法:</p> <ol> <li><code>"mergeImgHack": true,</code> あるいは高さ決め打ちならば <code>"mergeImgHack": 1024,</code> のような値を <code>backstop.json</code> のルートに追記する</li> <li>beta版を使用する</li> </ol> <p>1.は効果がなかったので2.を試そうと思ったのですが、 Github のブランチ (<code>2.6.7</code>)と npm で指定した場合(<code>4.3.3</code>)で Beta のバージョンが異なる模様。</p> <p><code>package.json</code> の <code>dependencies</code> や <code>devDependencies</code> に <code>"backstopjs": "https://github.com/garris/BackstopJS#beta"</code> と指定すると <code>2.6.7</code>、<code>"backstopjs": "beta",</code> と指定すると <code>4.3.3</code> が落ちてきます。</p> <p>そこで各バージョンで何度か試行錯誤を繰り返しました。</p> <ul> <li><code>5.0.7</code>: <ul> <li>最新版</li> <li>背景画像が引き伸ばされる現象が発生する</li> <li>engine で chromy を指定しようとしたところ、 <code>createBitmaps | Chromy is no longer supported in version 5+. Please use version 4.x.x for chromy support.</code> とのこと。 <code>2.6.7</code> ではそこそこそれっぽい表示になったので <code>engine</code> を切り替えることができれば……と思ったのですが、ダメそうです</li> </ul></li> <li><code>4.3.3</code>: <ul> <li>背景画像が引き伸ばされる現象の解決には至らず</li> <li>いろいろ今どきに近くなっているので各種設定はしやすい</li> </ul></li> <li><code>2.6.7</code>: <ul> <li>昨日別の場所で試したときは動いたのですが動作再現できず</li> <li>内部的に PhantomJS を使用している模様。2018年に開発終了しているので今更使いたくはない</li> <li>設定を node script から渡すとき、オブジェクトではなくファイルパスを渡す模様。とすると、いったん <code>backstop.json</code> を書き出す処理を挟まないといけない</li> </ul></li> </ul> <p>よくよく先程のスレッドを見直してみると <code>npm install backstopjs@beta</code> とか <code>4.3.3</code> と言ったワードがあるので普通に npm 指定の方で良さそうですね。</p> <p>ひとまず <code>devDependencies</code> に <code>"backstopjs": "beta"</code> を指定してまた色々試してみたいと思います。</p> <h2 id="備考"><a href="#%E5%82%99%E8%80%83">備考</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://swet.dena.com/entry/2018/04/26/152326">Puppeteerによるフルページスクリーンショットを画像遅延読み込みに対応させる - DeNA Testing Blog</a></li> </ul> <p>スクロールに応じてコンテンツが表示される系のアニメーションもあるので、「下端までスクロールしてからスクリーンショットを撮影する」も対応したいですね……。</p> <h2 id="追記(2020/12/20)"><a href="#%E8%BF%BD%E8%A8%98%282020%2F12%2F20%29">追記(2020/12/20)</a></h2> <p>色々試行錯誤した結果のリポジトリを晒しておきます。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/arm-band/test_backstop_randa_back_stopper_2">arm-band/test_backstop_randa_back_stopper_2: BackstopJS のテストです。</a></li> </ul> <p>これは本当、どうしたものか……。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/garris/BackstopJS/issues/785">Issues using vh units in CSS ・ Issue #785 ・ garris/BackstopJS</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/garris/BackstopJS/issues/820">Take screenshot without resizing page ・ Issue #820 ・ garris/BackstopJS</a></li> </ul> <h3 id="スクリプト"><a href="#%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88">スクリプト</a></h3> <h4 id="複数枚のスクリーンショットを繋ぎ合わせる"><a href="#%E8%A4%87%E6%95%B0%E6%9E%9A%E3%81%AE%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88%E3%82%92%E7%B9%8B%E3%81%8E%E5%90%88%E3%82%8F%E3%81%9B%E3%82%8B">複数枚のスクリーンショットを繋ぎ合わせる</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/garris/BackstopJS/issues/820#issuecomment-404963122">Take screenshot without resizing page ・ Issue #820 ・ garris/BackstopJS</a></li> </ul> <h4 id="下端までスクロールさせる"><a href="#%E4%B8%8B%E7%AB%AF%E3%81%BE%E3%81%A7%E3%82%B9%E3%82%AF%E3%83%AD%E3%83%BC%E3%83%AB%E3%81%95%E3%81%9B%E3%82%8B">下端までスクロールさせる</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://swet.dena.com/entry/2018/04/26/152326">Puppeteerによるフルページスクリーンショットを画像遅延読み込みに対応させる - DeNA Testing Blog</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://gist.github.com/nnks1010/10abf3032933609d1b2bd5b99fc12586">絶対顔本スクレイピングするマン</a></li> </ul> <h4 id="Node.js での実行中スクリプトの絶対パス"><a href="#Node.js+%E3%81%A7%E3%81%AE%E5%AE%9F%E8%A1%8C%E4%B8%AD%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E3%81%AE%E7%B5%B6%E5%AF%BE%E3%83%91%E3%82%B9">Node.js での実行中スクリプトの絶対パス</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://gist.github.com/uupaa/da42698d6b2d2cbb3cca">node.js で絶対パスや相対パスを取得する方法 npm __dirname</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/16236 2020-11-24T23:34:09+09:00 2020-11-24T23:34:09+09:00 https://crieit.net/posts/gulp4-webpack5-terser-webpack-plugin-error-resolutions-20201124 続・ Gulp 4 + Webpack 5 を試す (resolutions 使用) <p>以前、<a href="https://crieit.net/posts/gulp4-webpack5-terser-webpack-plugin-error-20201020">Gulp 4 + Webpack 5 を試す ( 未完 / terser-webpack-plugin で TypeError: Cannot read property ‘javascript’ of undefined エラーになる)</a>の記事で Gulp 4 + Webpack 5 の実験をしましたが、その続きです。</p> <h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2> <p><a href="https://crieit.net/posts/backstopjs-test-20201122">BackstopJS を試す (Error: Failed to launch the browser process! エラー発生→ puppeteer のバージョンを指定して解決)</a>で <code>package.json</code> に <code>resolutions</code> を記述して Yarn で内部依存パッケージのバージョンを強制的に変更する手法を取りましたが、同様の手法が使えるのではないか、と思った次第です。</p> <h2 id="検証"><a href="#%E6%A4%9C%E8%A8%BC">検証</a></h2> <h3 id="package.json"><a href="#package.json">package.json</a></h3> <p>```json:package.json<br /> // 略<br /> "devDependencies": {<br /> // 略<br /> "webpack": "^5.6.0",<br /> "webpack-stream": "^6.1.1",<br /> "terser-webpack-plugin": "^5.0.3",<br /> // 略<br /> },<br /> "resolutions": {<br /> "webpack": "^5.6.0"<br /> },<br /> // 略</p> <pre><code><br />上述のように `resolutions` で Webpack のバージョンを指定。 ### gulp/tasks/js.js `gulp/tasks/js.js` は前回のまま。 ### webpack.config.js `webpack.config.js` は source map のための設定を追加した以外はそのままです。 ```javascript:webpack.config.js const _ = require('./gulp/plugin'); const dir = require('./gulp/dir'); const mode = () => { return process.env.DEV_MODE === 'dev' ? 'development' : 'production'; }; const modeFlag = () => { return process.env.DEV_MODE === 'dev' ? false : true; }; const entry = () => { const entries = _.glob .sync( '**/*.js', { ignore: [ '_plugins/**' ], cwd: dir.src.js } ) .map(function (key) { return [key, _.path.resolve(dir.src.js, key)]; }); return Object.fromEntries(entries) }; const configs = { mode: mode(), entry: entry(), output: { filename: '[name]' }, optimization: { minimizer: [ new _.webpackTerser({ extractComments: 'some', terserOptions: { compress: { drop_console: modeFlag(), }, }, }), ], } }; if (process.env.DEV_MODE === 'dev') { // 追加 configs.devtool = 'inline-source-map'; } module.exports = configs; </code></pre> <p>これで <code>yarn restart</code> などすると</p> <pre><code class="bash">$ gulp # 略 [hh:ii:ss] asset app.js 226 KiB [emitted] [minimized] (name: app.js) 1 related asset webpack 5.6.0 compiled successfully [hh:ii:ss] Finished 'jsBuild' after 20 s </code></pre> <p>動きました!</p> <hr /> <p>後々は <code>resolutions</code> なしでも動くようにしたいですが、ひとまず動く形になったのでメモしておきます。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <h3 id="Yarn の reasolutions"><a href="#Yarn+%E3%81%AE+reasolutions">Yarn の reasolutions</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://nju33.com/yarn/resolutions%20%E3%81%A7%E4%BE%9D%E5%AD%98%E3%81%AE%E4%BE%9D%E5%AD%98%E3%81%AE%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3%E3%82%92%E6%8C%87%E5%AE%9A%E3%81%99%E3%82%8B">yarn: resolutions で依存の依存のバージョンを指定する - nju33</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/16235 2020-11-24T23:28:18+09:00 2022-02-25T22:47:24+09:00 https://crieit.net/posts/gulp4-webpack5-terser-webpack-plugin-error-20201020 Gulp 4 + Webpack 5 を試す ( 未完 / terser-webpack-plugin で TypeError: Cannot read property 'javascript' of undefined エラーになる) <p>以前、<a target="_blank" rel="nofollow noopener" href="https://labor.ewigleere.net/2020/10/06/browserify-babelify-gulp-transpile-ie/">browserify + babelify + Gulp で IE11対応を試す</a>の記事で browserify + babelify を試しましたが、今回は Webpack に挑んでみます。</p> <p>ただし、プレーンな Gulp 環境ではなく、 <a target="_blank" rel="nofollow noopener" href="https://github.com/arm-band/kiribi_ususama/tree/8a06ef719725236c26337a71bb2b1f9b64ef0900">Ususama</a> で。</p> <h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2> <h3 id="package.json"><a href="#package.json">package.json</a></h3> <pre><code class="json">// 略 "devDependencies": { // 略 "glob": "7.1.6", "webpack": "^5.1.3", "webpack-stream": "^6.1.0", "terser-webpack-plugin": "^5.0.0", // 略 } // 略 </code></pre> <p><code>gulp-uglify-es</code> と <code>gulp-concat</code> を除き、代わりに <code>webpack</code> 本体と Gulp で Webpack を使用するために必要な <code>webpack-stream</code> 、そして minifier の terser の Webpack 用プラグインである <code>terser-webpack-plugin</code> を追加。</p> <h3 id="gulp/tasks/js.js"><a href="#gulp%2Ftasks%2Fjs.js">gulp/tasks/js.js</a></h3> <pre><code class="javascript">const gulp = require('gulp'); const plumber = require('gulp-plumber'); const notify = require('gulp-notify'); const webpack = require('webpack'); const webpackStream = require('webpack-stream'); const rename = require('gulp-rename'); const dir = { dist: { js: './dist/js' } }; const webpackConfig = require('../../weppack.config'); const jsBuild = () => { return _.webpackStream(webpackConfig) .pipe(_.plumber({ errorHandler: _.notify.onError({ message: 'Error: <%= error.message %>', title: 'jsLibBuild' }) })) .pipe(_.rename((path) => { path.basename += '.min' path.extname = '.js' })) .pipe(_.gulp.dest(dir.dist.js)); }; module.exports = jsBuild; </code></pre> <h3 id="webpack.config.js"><a href="#webpack.config.js">webpack.config.js</a></h3> <pre><code class="javascript">const webpack = require('webpack'); const webpackTerser = require('terser-webpack-plugin'); const path = require('path'); const glob = require('glob'); const dotenv = require('dotenv').config(); const dir = { src: { js: './src/js' } }; const mode = () => { return process.env.DEV_MODE === 'dev' ? 'development' : 'production'; }; const modeFlag = () => { return process.env.DEV_MODE === 'dev' ? false : true; }; const entry = () => { const entries = _.glob .sync( '**/*.js', { ignore: [ '_plugins/**' ], cwd: dir.src.js } ) .map(function (key) { return [key, _.path.resolve(dir.src.js, key)]; }); return Object.fromEntries(entries) }; module.exports = { mode: mode(), entry: entry(), output: { filename: '[name]' }, optimization: { minimizer: [ new _.webpackTerser({ extractComments: 'some', terserOptions: { compress: { drop_console: modeFlag(), }, }, }), ], } }; </code></pre> <p>いくつかの記事を参考にしながらタスクを組みます。</p> <p>自分でカスタマイズした部分は以下。</p> <ul> <li><code>.env</code> で <code>dev</code>, <code>demo</code>, <code>prod</code> のモードを切り替えているので、その部分を <code>process.env.DEV_MODE</code> で振り分け <ul> <li>Webpack の設定の <code>mode</code> と <code>terser-webpack-plugin</code> の <code>drop_console</code> のフラグが関係しています</li> </ul></li> <li>複数の <code>.js</code> ファイルをエントリポイントにしたかったのでその部分は<a target="_blank" rel="nofollow noopener" href="https://qiita.com/masato_makino/items/7130bbe408ca929e7f0d">webpackのentryファイルを複数指定、globパッケージの使い方 - Qiita</a>を参考に</li> <li>最終的なファイル名は <code>XXX.min.js</code> の形にしたかったので <code>gulp-rename</code> を通しました</li> </ul> <h2 id="jQuery の扱い"><a href="#jQuery+%E3%81%AE%E6%89%B1%E3%81%84">jQuery の扱い</a></h2> <p>タスク自体は上記のやり方で走ることが確認できました( <code>DEV_MODE=dev</code> )。</p> <p>次は現時点ではまだ jQuery を使用しているので、 jQuery をどう読み込ませるかが課題ですが、以下のようにして動作することを確認しました。</p> <h3 id="app.js"><a href="#app.js">app.js</a></h3> <pre><code class="javascript">import $ from 'jquery'; import 'jquery.easing/jquery.easing'; $(() => { /* 処理 */ }); </code></pre> <h3 id="sitesearch.js"><a href="#sitesearch.js">sitesearch.js</a></h3> <pre><code class="javascript">import $ from 'jquery'; import List from 'list.js'; //サイト内検索 export default () => { const options = { valueNames: ['searchTitle', 'searchText'], }; const searchList = new List('listSearch', options); //hits searchList.on('searchComplete', function (a) { $('#hits').text(a.matchingItems.length); }); }; </code></pre> <p>サイト内検索で <a target="_blank" rel="nofollow noopener" href="https://listjs.com">List.js</a> を使用しているのですが、これについては<a target="_blank" rel="nofollow noopener" href="https://github.com/javve/list.js/issues/559">How am I suppose to import list.js with es6 and webpack ? · Issue #559 · javve/list.js</a>の Issues の方法で解決しました。</p> <p>ここまでは比較的順調でした。</p> <p>しかし、間も無く壁に突き当たることになります……。</p> <h2 id="TypeError: Cannot read property 'javascript' of undefined エラー"><a href="#TypeError%3A+Cannot+read+property+%27javascript%27+of+undefined+%E3%82%A8%E3%83%A9%E3%83%BC">TypeError: Cannot read property 'javascript' of undefined エラー</a></h2> <p><code>DEV_MODE=dev</code> で動作することは確認できたので、 <code>DEV_MODE=prod</code> に切り替えました。</p> <p>すると、以下のエラーが出てしまいました。</p> <pre><code>TypeError: Cannot read property 'javascript' of undefined at PATH\TO\PROJECT\node_modules\terser-webpack-plugin\dist\index.js:366:38 </code></pre> <p><code>DEV_MODE=dev</code> に戻すと先ほどと同じように問題なく動作。上記でこのフラグが関係するのは <code>mode</code> と <code>terser-webpack-plugin</code> の <code>drop_console</code> の2箇所なので、そのどちらかだろうとアタリを付けます。</p> <p>試しに <code>drop_console</code> を常に <code>false</code> としましたが、 <code>DEV_MODE=prod</code> でエラーは再現しました。</p> <p>となると、 <code>mode</code> の方ということになります。</p> <p>ここでエラー文で検索すると、以下の Issues を発見。 <code>terser-webpack-plugin</code> 本家のリポジトリです。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/webpack-contrib/terser-webpack-plugin/issues/335">TypeError: Cannot read property 'javascript' of undefined ・ Issue #335 ・ webpack-contrib/terser-webpack-plugin</a></li> </ul> <p>発生個所も含めてエラー文が同じです。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/webpack-contrib/terser-webpack-plugin/issues/335#issuecomment-709997726">TypeError: Cannot read property 'javascript' of undefined · Issue #335 · webpack-contrib/terser-webpack-plugin</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/webpack-contrib/terser-webpack-plugin/issues/335#issuecomment-710004332">TypeError: Cannot read property 'javascript' of undefined ・ Issue #335 ・ webpack-contrib/terser-webpack-plugin</a></li> </ul> <p>別の方のコメントを見ると、原因は以下の模様。</p> <ul> <li>terser-webpack-plugin 5 は Webpack 4 とは互換性がない</li> <li><code>webpack-stream</code> の内部で使用している Webpack がバージョン 4 系</li> </ul> <blockquote> <p>yep, we are working on it, release with fix will be today/tomorrow</p> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/webpack-contrib/terser-webpack-plugin/issues/335#issuecomment-710032380">TypeError: Cannot read property 'javascript' of undefined ・ Issue #335 ・ webpack-contrib/terser-webpack-plugin</a> (2020/10/16日 22:04 JST)</p> </blockquote> <p>わりとタイムリーなものを踏んでしまったようなので、 fixed されるのを待つ感じですかね……。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <h2 id="Gulp + Webpack"><a href="#Gulp+%2B+Webpack">Gulp + Webpack</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/tonkotsuboy_com/items/2d4f3862e6d05dc0bea1">Gulpで始めるwebpack 4入門 - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/am10/items/2516fa04def815195ffe">gulp + webpack + babelをつかってみた - Qiita</a></li> </ul> <h3 id="webpack-stream"><a href="#webpack-stream">webpack-stream</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.npmjs.com/package/webpack-stream">webpack-stream - npm</a></li> </ul> <h2 id="Webpack, 複数エントリポイントとoutput"><a href="#Webpack%2C+%E8%A4%87%E6%95%B0%E3%82%A8%E3%83%B3%E3%83%88%E3%83%AA%E3%83%9D%E3%82%A4%E3%83%B3%E3%83%88%E3%81%A8output">Webpack, 複数エントリポイントとoutput</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/masato_makino/items/7130bbe408ca929e7f0d">webpackのentryファイルを複数指定、globパッケージの使い方 - Qiita</a></li> </ul> <h2 id="List.js import"><a href="#List.js+import">List.js import</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/javve/list.js/issues/559">How am I suppose to import list.js with es6 and webpack ? · Issue #559 · javve/list.js</a></li> </ul> <h2 id="terser-webpack-plugin"><a href="#terser-webpack-plugin">terser-webpack-plugin</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/webpack-contrib/terser-webpack-plugin/issues/335">TypeError: Cannot read property 'javascript' of undefined ・ Issue #335 ・ webpack-contrib/terser-webpack-plugin</a></li> </ul> arm-band