tag:crieit.net,2005:https://crieit.net/tags/tailwind/feed 「tailwind」の記事 - Crieit Crieitでタグ「tailwind」に投稿された最近の記事 2022-08-31T09:39:51+09:00 https://crieit.net/tags/tailwind/feed tag:crieit.net,2005:PublicArticle/18288 2022-08-31T09:35:31+09:00 2022-08-31T09:39:51+09:00 https://crieit.net/posts/Web-ankeyto 【個人開発】作るのも、答えるのも簡単なWebアンケート「ankeyto」を作りました! <h1 id="2つ目の個人開発サービス公開"><a href="#2%E3%81%A4%E7%9B%AE%E3%81%AE%E5%80%8B%E4%BA%BA%E9%96%8B%E7%99%BA%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E5%85%AC%E9%96%8B">2つ目の個人開発サービス公開</a></h1> <p>アプリケーションのモダン化を勉強しようと個人開発を始めて、以下の記事のように公開していました。もう振り返ると1年経つんですね… こういった個人開発活動を続けることにより、少しずつ知識も身についてきて、開発スピードも上がってきていました。</p> <ul> <li><p>Github Issuesをキレイに外部公開するサービス「2go」作ってみた</p> <ul> <li>https://qiita.com/nice2have/items/28449ae4ef45fef2c671</li> </ul></li> <li><p>海外進出を目指して、ProductHuntへ個人開発サービスを投稿するまでにやったこと&やった結果を全面的にシェアする</p> <ul> <li>https://qiita.com/nice2have/items/f59a27c266efb9b8821c</li> </ul></li> </ul> <p>実際には公開していないのですが、atodyという「Twitterでいいねしたツイートに含まれるリンク・画像だけを自動でまとめてブックマーク化できるサービス」を開発したのですが、いまいちサービスの完成度に納得がいっていなく、開発したサービスとしては3つとなったのですが、公開したサービスは2つとなります。</p> <p><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1997184/3f2dbeed-c2cb-8437-164e-78a8797ea9df.png" alt="ogp.png" /></p> <h1 id="簡単Webアンケート"><a href="#%E7%B0%A1%E5%8D%98Web%E3%82%A2%E3%83%B3%E3%82%B1%E3%83%BC%E3%83%88">簡単Webアンケート</a></h1> <p>今回は会社の一定の範囲で簡単にアンケートを取りたいときに、Google FormsやMicrosoft Formsを作るのは手間だな、もっとライトな感じで反応を知りたいなと思ったのがきっかけです。</p> <p>また、アンケートの回答を得るまでのハードルが結構高いと思ったんです。アンケートURLにアクセスして、回答を入力して、、、となると、答えてくれる人が少なくなってしまって、アンケートの価値が薄まってしまうような気がしたんですよね。</p> <h1 id="機能を最小限に絞ったアンケートをリリースしよう"><a href="#%E6%A9%9F%E8%83%BD%E3%82%92%E6%9C%80%E5%B0%8F%E9%99%90%E3%81%AB%E7%B5%9E%E3%81%A3%E3%81%9F%E3%82%A2%E3%83%B3%E3%82%B1%E3%83%BC%E3%83%88%E3%82%92%E3%83%AA%E3%83%AA%E3%83%BC%E3%82%B9%E3%81%97%E3%82%88%E3%81%86">機能を最小限に絞ったアンケートをリリースしよう</a></h1> <p>これらの課題を解決しようと、ankeytoというアンケート機能を最小限に絞ったサービスを開発しようということに至りました。ankeytoはアンケートのモジりで「アンキート」と名付けることにしました。ちなみに、先程のatodyも「あとで」のモジりで「アトディ」と名付けています。</p> <p><a target="_blank" rel="nofollow noopener" href="https://ankeyto.com">https://ankeyto.com</a></p> <p>開発する前の大まかなコンセプトは、A3ノートにまとめる用にしているのですが、汚い字で申し訳ないですが、公開してみます。<br /> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1997184/8871aa10-08b9-76f6-447b-42611f8a02a4.jpeg" alt="IMG_1337.jpg" /><br /> サービスのコンセプトは以下の2つと設定しました。<br /> + 最大3つまでの選択肢<br /> + 選択肢ごとのURLにアクセスすればもう回答済みになる<br /> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1997184/ec5dcfde-7952-6f6b-2aa7-679b603afbd2.png" alt="image.png" /><br /> そのため、選択肢を表示して回答ができるページをSNSでシェアするのではなく、アクセスしたら回答できるURLをSNSでシェアするだけで良くなるのです。例えば、「これが良いと思ったら、このURLにアクセスして!」と言えば良くなり、アンケート回答のステップとハードルを減らすことができるのです。つまり、個人個人で対象への「いいね」を調査・獲得できるイメージです。<br /> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1997184/29d7fd65-e416-566f-3b3c-9345e146f755.png" alt="image.png" /></p> <h1 id="画面イメージ"><a href="#%E7%94%BB%E9%9D%A2%E3%82%A4%E3%83%A1%E3%83%BC%E3%82%B8">画面イメージ</a></h1> <p>こちらのようにアンケート回答がリアルタイムにグラフやUIに反映されるので、回答が多いと変化を楽しめるかもしれません。例えば、プレゼン最中の意見を軽く聞きたいときや、勉強会のフィードバックを簡単に得たいときなどに利用できるかもしれません。<br /> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1997184/12cb36a5-f89b-8aad-dded-dffcc5f7c223.gif" alt="demo.gif" /></p> <p>アンケートを作るときの画面も極力シンプルにしています。こちらもログインが必要だと手間が増えてしまったり、入力項目数が多いと途中で離脱してしまう可能性が高くなってしまうので、最小限にすることを意識しています。<br /> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1997184/737aa90d-06ef-4ebc-a439-1dcfc31e9e66.png" alt="image.png" /></p> <h1 id="利用技術"><a href="#%E5%88%A9%E7%94%A8%E6%8A%80%E8%A1%93">利用技術</a></h1> <p>個人開発者としてはまだまだペーペーなので、利用技術も運用負荷が最小限になることを意識しています。<br /> - Firebase(Firestore/Functions/Hosting)<br /> - Vue.js<br /> - Tailwindcss<br /> - Slack</p> <p>サービス自体がとてもシンプルなので、HostingでWebページを表示し、質問作成や回答をFunctionsで処理し、結果をFirestoreでリアルタイムに反映しているだけです。一応、モチベーション維持のために、質問が作成されたり、回答されたりしたら、FuncitonsからSlackに通知されるようにしています。</p> <h1 id="将来に向けて"><a href="#%E5%B0%86%E6%9D%A5%E3%81%AB%E5%90%91%E3%81%91%E3%81%A6">将来に向けて</a></h1> <p>最後に、将来は以下のロードマップを考えています。<br /> - URLにアクセスするよりもさらにハードルを下げるために、URLをQRコード化できるように。<br /> - Cookie等を利用した重複回答者の防止機能<br /> - 質問IDをランダムでなく、わかりやすいIDを提供できる機能<br /> - API機能(UIだけでなく、いいねAPIを提供)<br /> - 英語対応</p> <h1 id="最後に。"><a href="#%E6%9C%80%E5%BE%8C%E3%81%AB%E3%80%82">最後に。</a></h1> <p>まさにこのankeytoを利用して、このWebサービスってどうですか?とアンケートを取って終われればと思います。以下が質問ページから、SNS共有用にコピーできる文章になります。「いい!」か「まあまあ」のURLにアクセスすれば回答が完了しますので、ぜひよろしくお願いします!</p> <blockquote> <p>Q. このWebサービスってどうですか?<br /> https://ankeyto.com/q/BbDJA3P11</p> <p>いい! : https://ankeyto.com/BbDJA3P11/1<br /> まあまあ : https://ankeyto.com/BbDJA3P11/2</p> </blockquote> <p>また、個人開発の進捗などを垂れ流すTwitterもやっていますので、こちらもよろしければフォローお願いします。<br /> <a target="_blank" rel="nofollow noopener" href="https://twitter.com/jnakajima1982">https://twitter.com/jnakajima1982</a></p> jnakajima1982 tag:crieit.net,2005:PublicArticle/18231 2022-06-26T14:43:49+09:00 2022-06-26T14:52:21+09:00 https://crieit.net/posts/Subreco-3 サブスクの解約忘れ・無駄遣いを防止するサービス「Subreco」を開発して得た3つの学び <h2 id="筆者について"><a href="#%E7%AD%86%E8%80%85%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6">筆者について</a></h2> <p>「多くのユーザーが実際に使って、価値があるものはなんだろう?」と考えながら、個人開発を趣味ではなく「<strong>収益を出す</strong>」ために行なっています。</p> <p>技術だけでなく、デザインやグロースハックも好きで、色々な視点からWebサービスを作り上げていくのが好きです。</p> <p>今回ご紹介する「<strong><a target="_blank" rel="nofollow noopener" href="https://bit.ly/3niPqoN">Subreco</a></strong>」は個人開発を始めて3つ目のサービスとなります。</p> <p><strong><a target="_blank" rel="nofollow noopener" href="https://bit.ly/3niPqoN">Subrecoの公式サイトはこちら</a></strong></p> <h2 id="なぜ、サブスク管理サービス「Subreco」を作ったのか?"><a href="#%E3%81%AA%E3%81%9C%E3%80%81%E3%82%B5%E3%83%96%E3%82%B9%E3%82%AF%E7%AE%A1%E7%90%86%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%80%8CSubreco%E3%80%8D%E3%82%92%E4%BD%9C%E3%81%A3%E3%81%9F%E3%81%AE%E3%81%8B%EF%BC%9F">なぜ、サブスク管理サービス「Subreco」を作ったのか?</a></h2> <p><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326850/ea6daaa9-5fae-ecc7-c8f1-3a99368d62e8.png" alt="スクリーンショット 2022-06-26 12.44.09.png" /><br /> 自分自身、何度もサブスクの「<strong>解約忘れ</strong>」で失敗した経験があったからです。</p> <p>あなたも、サブスクなどで使っていないサービスを解約し忘れて"<strong>無駄な支払いをしてしまった経験</strong>"ありませんか?</p> <p>家計簿アプリ、クレジットカード会社が提供しているアプリは時々見ていたものの、「<strong>あっこのサービス使ってないのに解約し忘れたぁ!(絶望…)</strong>」ということが何回かあったんですね。</p> <p>1回だけならいいのですが。何回も経験したので「さすがにどうにかしたいなぁ」と思って、次はメモアプリとカレンダーアプリを併用して使っていました。</p> <p>これでサブスクと契約更新日を両方管理できる!対策バッチリ!</p> <p><strong>、、、と思いきや、だんだんメモとカレンダーアプリで二重管理するのが面倒くさくなり、結果的に続かなかったんです。。</strong></p> <p>メモアプリやカレンダーアプリだと「<strong>毎回今月は〇〇を使っていたっけ…?</strong>」とわざわざ自分が使っているサービスを"<strong>意識</strong>"して見直さなければなりません。</p> <p>でも、そんなの毎回意識できないし、面倒ですよね?</p> <p>この経験から、使っているサブスクを「<strong>定期的に見直す仕組みを自動化</strong>」しようと生まれたのがSubrecoです。</p> <h2 id="Subrecoの特徴"><a href="#Subreco%E3%81%AE%E7%89%B9%E5%BE%B4">Subrecoの特徴</a></h2> <p><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326850/9498e705-0014-ad3a-e72c-12981a941dc5.png" alt="スクリーンショット 2022-06-23 13.28.50.png" /><br /> Subrecoの使い方はシンプルに3STEPで完結します。</p> <pre><code>①今使っている全てのサブスクを一括管理して把握する ②各サブスクの契約更新日があるたびに自動通知する ③通知があるたびに「利用継続」or 「利用停止」を判断する </code></pre> <p>契約更新日はメールで自動通知するため、わざわざメモを見直したり、毎回カレンダーで契約更新日を設定し直さなくて済むのがポイント。</p> <p>使っているサブスクを登録しておくだけで、自動でSubrecoが契約更新日をお知らせします。</p> <p><strong><a target="_blank" rel="nofollow noopener" href="https://bit.ly/3niPqoN">Subrecoの公式サイトはこちら</a></strong></p> <h2 id="「Subreco」を作って得た学び"><a href="#%E3%80%8CSubreco%E3%80%8D%E3%82%92%E4%BD%9C%E3%81%A3%E3%81%A6%E5%BE%97%E3%81%9F%E5%AD%A6%E3%81%B3">「Subreco」を作って得た学び</a></h2> <pre><code>①慣れないことはしない ②コア機能を徹底的に絞る ③自分は使うのかを考える </code></pre> <p>ここからは実際に<a target="_blank" rel="nofollow noopener" href="https://bit.ly/3niPqoN">Subreco</a>を開発して、得た学びを3つ紹介します。</p> <h3 id="①慣れないことはしない"><a href="#%E2%91%A0%E6%85%A3%E3%82%8C%E3%81%AA%E3%81%84%E3%81%93%E3%81%A8%E3%81%AF%E3%81%97%E3%81%AA%E3%81%84">①慣れないことはしない</a></h3> <p>実は、当初SubrecoはNuxt.jsで作っていました。(ちなみにNuxt.jsを使うのが今回が始めて)</p> <p>最初は順調だったものの、徐々にハマるポイントが多く、開発スピードが落ちてきたので、思い切ってLaravel×Vue.jsでリプレイスしました。</p> <p>結果、開発スピードは<strong>3倍</strong>以上にUP。</p> <p>ここから分かったのは、収益目的で個人開発をする場合、<strong>絶対に自分が慣れているフレームワークで開発すべき</strong>だということ。</p> <p>ただでさえ開発以外にもすべきこと(デザイン、マーケetc.)が多いのに、開発に負荷がかかる進め方は合理的でないと痛感しました。</p> <h3 id="②コア機能を徹底的に絞る"><a href="#%E2%91%A1%E3%82%B3%E3%82%A2%E6%A9%9F%E8%83%BD%E3%82%92%E5%BE%B9%E5%BA%95%E7%9A%84%E3%81%AB%E7%B5%9E%E3%82%8B">②コア機能を徹底的に絞る</a></h3> <p>テレビのリモコンを想像してみましょう。</p> <p><code>・電源の入/切ができる</code><br /> <code>・チャンネルで主要な局が切り替えられる</code></p> <p>一番使う機能が多いのは上記だと思います。</p> <p>しかし、実際にテレビのリモコンを見ると他にもボタンがありますよね。<br /> で、実際にほとんどのボタンを使っていないという方も多いのではないでしょうか?</p> <p>そう。<strong>実際にユーザーが求めている機能はシンプル</strong>であることが多いです。</p> <p>これはWebサービスも同じ。</p> <p><a target="_blank" rel="nofollow noopener" href="https://bit.ly/3niPqoN">Subreco</a>の場合、</p> <pre><code>・サブスクの管理が一覧で見れること ・カレンダー形式で契約更新日を確認できること ・契約更新日を自動でメール通知してくれること </code></pre> <p>これさえあれば、<strong>Subrecoが提供したい価値の必要条件は満たせている</strong>と判断して実装していました。</p> <p>実装前に大事なのは、Webサービスの機能として「<strong>必要条件</strong>」と「<strong>十分条件</strong>」を分けて考えること。</p> <p>これは過去の自分に何回も言い聞かせたいです。</p> <h3 id="③自分は使うのかを考える"><a href="#%E2%91%A2%E8%87%AA%E5%88%86%E3%81%AF%E4%BD%BF%E3%81%86%E3%81%AE%E3%81%8B%E3%82%92%E8%80%83%E3%81%88%E3%82%8B">③自分は使うのかを考える</a></h3> <p>個人開発でアイディアを出す際に、</p> <pre><code>①自分起点・・・自分の課題を解決するアイディア ②相手起点・・・自分以外の人が抱えている課題を解決するアイディア </code></pre> <p>の2パターンがあります。</p> <p>今回思ったのが、個人開発をするという前提に立って言えば、圧倒的に<strong>自分起点</strong>で開発するのが良いと思いました。</p> <p>なぜなら、スタート地点で需要が少なくとも「<strong>1つ</strong>」は存在しているから。<br /> 需要が0の場合はそこからスケールさせようがないですが、1であればスケールさせられる可能性は残っています。</p> <p>また、1番のヘビーユーザーは自分になるため、あった方が良い機能や操作する中で潜在的なバグに気付くことができます。<br /> 最悪、誰も使わなくても自分を助けてくれるツールとして機能するので"<strong>一石三鳥</strong>"ですね。</p> <h2 id="まとめ"><a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></h2> <p><img src="https://storage.googleapis.com/zenn-user-upload/62663bd93952-20220626.png" alt="" /></p> <p>収益目的で個人開発をすると考えることは膨大にあります。</p> <p>もしかしたら、あなたも「<strong>個人開発で収益を出したい!</strong>」と、もがいて、あがいている途中かもしれません。</p> <p>僕もあなたと一緒です...!</p> <p>ただ、辛いことや苦しいことがつきものの個人開発ですが、その過程で得られる</p> <p><strong>「うひょ〜!」という小さな成功体験</strong>(考えていた機能が完成した時etc.)<br /> <strong>「よし進んでるな」という実感(Todoリストにチェックを入れる瞬間)</strong></p> <p>これらは<strong>圧倒的な揺るぎない自信、資産</strong>として必ず残っていきます。</p> <p>だからこそ、個人開発を続けている方は諦めないで、一緒に頑張りましょう!</p> <p>僕は引き続き<a target="_blank" rel="nofollow noopener" href="https://bit.ly/3niPqoN">Subreco</a>の改善を続けていきます!ではでは!</p> <p><strong><a target="_blank" rel="nofollow noopener" href="https://bit.ly/3niPqoN">Subreco</a>を実際に利用してみて、ご要望・感想などありましたら、メッセージ頂けると嬉しいです!^^</strong></p> サイゼン tag:crieit.net,2005:PublicArticle/17641 2021-09-10T06:00:03+09:00 2021-09-10T06:00:03+09:00 https://crieit.net/posts/Github-Issues-2go Github Issuesをキレイに外部公開するサービス「2go」作ってみた <h1 id="はじめに"><a href="#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB">はじめに</a></h1> <p>今回初めて個人開発で作ったサービスを公開します。今までも何回かトライしていたのですが、なかなか公開までに至らないうちに、なにか問題にぶちあたったり、時間がかかりすぎて情熱が冷めてしまったりしていたので、今回はまずはスモールスタートで公開して改善していくことを心がけました。大体今回の公開まで、開発を始めてから1ヶ月程度になります。毎日朝4時半に起きて、子どもたちが起きる7時過ぎまでを開発時間として取り組んできました。<br /> <img src="https://storage.googleapis.com/zenn-user-upload/1463631d38d985beee6c9289.png" alt="" /></p> <h1 id="どんな人向け?"><a href="#%E3%81%A9%E3%82%93%E3%81%AA%E4%BA%BA%E5%90%91%E3%81%91%EF%BC%9F">どんな人向け?</a></h1> <p>自分で作っているサービスの開発ロードマップをユーザーに公開するために、都度ブログを書いたりするのも大変ですよね。もし、自分のサービスのソースコードをGithubで管理していたとして、Github Issuesを見せるとしてもGithubに馴染みのない人にとっては読みにくいですし、外に出すサービスであればあるほど、Githubのレポジトリは非公開になっていると思います。これをなんとかできないかなと思いました。<br /> <img src="https://storage.googleapis.com/zenn-user-upload/4aad80e2913adc2dac582847.png" alt="" /></p> <h1 id="どんなサービス?"><a href="#%E3%81%A9%E3%82%93%E3%81%AA%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%EF%BC%9F">どんなサービス?</a></h1> <p>まずはこちらを見てください。<br /> <a target="_blank" rel="nofollow noopener" href="https://2go.plus/nice2h/2go/roadmap">https://2go.plus/nice2h/2go/roadmap</a><br /> このようにGithubのレポジトリ内のIssueをMilestone区切りにして、Githubに馴染みのない人にも見やすいロードマップサイトを公開するサービスです。もちろん、非公開のレポジトリを公開設定にすることなく、ロードマップだけを外部公開することができます。<br /> <img src="https://storage.googleapis.com/zenn-user-upload/18ca977fd4aa86432338179e.png" alt="" /><br /> Githubアカウントでログインすると、以下の設定画面になります。レポジトリを選択して、どのMilestoneを共有するかをチェックして保存します。すると、自動的にロードマップサイトのURLが生成されるので、こちらにアクセスすれば常に最新のロードマップを見ることができます。このURLをシェアすれば、多くの人にあなたのサービスのロードマップを簡単にキレイに公開できるわけです。<br /> <img src="https://storage.googleapis.com/zenn-user-upload/d3bf728edcb2592f63689133.png" alt="" /><br /> Milestoneを今まで使っていな方は、Milestoneを作成して、Issueを割り当てればロードマップを整理することができます。また、Labelを活用している方もいらっしゃると思いますので、Milestoneの中のLabelでIssueをフィルタすることもできます。これらを駆使することで、Bugは見せずにenhancementだけ表示するなどのことも可能です。<br /> 今後の予定としては、今はMilestone基点で表示しているところを、Labelベースで表示できるようにもしたいと思っています。その他は、まさに上記のURLを参考にしていただければ、いつも最新です。</p> <h1 id="どんな技術を使ってる?"><a href="#%E3%81%A9%E3%82%93%E3%81%AA%E6%8A%80%E8%A1%93%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%82%8B%3F">どんな技術を使ってる?</a></h1> <p>企業での開発経験はないので、全て自己流です。書籍やWebサイトを中心に勉強して開発しながら学んできました。念の為、利用している技術は以下のとおりです。<br /> - Laravel : APIとBlade(SSRはやってみたことあり)<br /> - Vue3 : フロントエンド(今回初挑戦)<br /> - Tailwindcss : デザイン(Bootstrapからの脱却)<br /> - Docker : これらの開発環境と、本番でもDocker利用(いずれ分割予定)<br /> - Cloud : Conoha VPS(インフラに時間を掛けたくなかった)</p> <h1 id="気になっていること(随時追加予定)"><a href="#%E6%B0%97%E3%81%AB%E3%81%AA%E3%81%A3%E3%81%A6%E3%81%84%E3%82%8B%E3%81%93%E3%81%A8%EF%BC%88%E9%9A%8F%E6%99%82%E8%BF%BD%E5%8A%A0%E4%BA%88%E5%AE%9A%EF%BC%89">気になっていること(随時追加予定)</a></h1> <ul> <li>開発環境はLaravel sailを使いましたが、本番は自前でdocker-compose.ymlを使いました。このあたりのコンテナの構成をどのようにしているか気になりました(本番でもsail使う?)</li> <li>本番環境にコードをpullしてからnpm run prodしてbuildが終わるまでにラグがあり、この間は使えない機能が出てしまったりするが、このあたりをみなさんはどのように工夫しているか?</li> <li>DBについては、どこまでレコードを暗号化するか。検索機能などとのトレードオフになると思われるが。。</li> </ul> <h1 id="今後は?"><a href="#%E4%BB%8A%E5%BE%8C%E3%81%AF%EF%BC%9F">今後は?</a></h1> <p>現在alphaフェーズですが、alphaとしてもう1段階リリースの予定があります。多くの人に使っていただき、Feedbackをいただきながら改善して、開発経験を増していきたいと思います。また、今回の個人開発は最初から海外も同様の問題を抱えていると思ったので、最初から英語圏も視野に入っており、今の所すべてのUIは英語にしています。以下の英語Twitterアカウントも作成して、情報発信していこうと思っています。</p> <p><a target="_blank" rel="nofollow noopener" href="https://twitter.com/2go_plus">https://twitter.com/2go_plus</a></p> <p>もちろん、日本語のTwitterは私の今まで通りのアカウントを利用していく予定です。<br /> <a target="_blank" rel="nofollow noopener" href="https://twitter.com/jnakajima1982">https://twitter.com/jnakajima1982</a><br /> ぜひご感想・ご指摘をいただければと思います。</p> jnakajima1982 tag:crieit.net,2005:PublicArticle/15870 2020-04-26T21:58:35+09:00 2020-06-28T13:07:35+09:00 https://crieit.net/posts/cargo-make cargo-makeによるプロジェクト・ビルド入門 <h1 id="cargo-makeによるプロジェクト・ビルド"><a href="#cargo-make%E3%81%AB%E3%82%88%E3%82%8B%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%83%BB%E3%83%93%E3%83%AB%E3%83%89">cargo-makeによるプロジェクト・ビルド</a></h1> <h2 id="モチベーション"><a href="#%E3%83%A2%E3%83%81%E3%83%99%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3">モチベーション</a></h2> <p>cargoはrustのパッケージ管理ツール兼ビルドツールである.これい自体非常に便利なのだが, Web標準は無視できない. 特にSeedのようなWebフロントエンド・フレームワークによる開発ではWeb標準に合わせる必要も出てくる. 単純なケースでは<a target="_blank" rel="nofollow noopener" href="https://seed-rs.org/guide/quickstart">Quickstart</a>に従ってindex.htmlに直接wasmモジュールを導入すれば良いのですが, 複雑なアプリなどはwebpackなどが使えた方が便利だと思います. 今回のケースではTailwindの導入などがそれに当たります.</p> <h2 id="目標"><a href="#%E7%9B%AE%E6%A8%99">目標</a></h2> <p>CSSフレームワークであるtailwindcssをSeedプロジェクトで利用する.</p> <h2 id="前提条件"><a href="#%E5%89%8D%E6%8F%90%E6%9D%A1%E4%BB%B6">前提条件</a></h2> <ul> <li>rustupの導入</li> </ul> <p>とりあえずこれを導入しておけば諸々の開発環境の導入・管理が行えるようになります.</p> <h2 id="NPM vs Cargo"><a href="#NPM+vs+Cargo">NPM vs Cargo</a></h2> <div class="table-responsive"><table> <thead> <tr> <th>機能</th> <th>NPM</th> <th>Cargo</th> <th>備考</th> </tr> </thead> <tbody> <tr> <td>パッケージ管理</td> <td>⭕️</td> <td>⭕️</td> <td>パッケージのインスタール・公開などができる</td> </tr> <tr> <td>依存性管理</td> <td>⭕️</td> <td>⭕️</td> <td>lockファイルがある点など共通点が多い</td> </tr> <tr> <td>タスク・ランナー</td> <td>⭕️</td> <td>❌</td> <td>Cargoではカスタム・コマンドが開発できる(はず)</td> </tr> <tr> <td>ビルド</td> <td>❌</td> <td>⭕️</td> <td>NPMでは代わりにタスク・ランナーを使う</td> </tr> <tr> <td>コマンド拡張</td> <td>❌</td> <td>⭕️</td> <td>タスク・ランナーから呼び出せば良い</td> </tr> </tbody> </table></div> <p>Cargoには<a target="_blank" rel="nofollow noopener" href="https://docs.npmjs.com/misc/scripts">npm-scripts</a>のようなタスク・ランナーがありませんが, <a target="_blank" rel="nofollow noopener" href="https://doc.rust-lang.org/cargo/reference/external-tools.html#custom-subcommands">カスタム・コマンド</a>で機能を拡張することができます. その一つが<a target="_blank" rel="nofollow noopener" href="https://github.com/sagiegurari/cargo-make">cargo-make</a>です.</p> <h2 id="cargo-makeによるプロジェクト管理"><a href="#cargo-make%E3%81%AB%E3%82%88%E3%82%8B%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E7%AE%A1%E7%90%86">cargo-makeによるプロジェクト管理</a></h2> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/seed-rs/seed-quickstart">seed-quickstart</a>のMakefile.toml内容を解説する感じです. wachモードなどは<a target="_blank" rel="nofollow noopener" href="https://github.com/brainvader/EvaQL/blob/tags/v0.1/ui/Makefile.toml#L73">EvaQL/ui/Makefile.toml</a>を参照してください.</p> <h3 id="cargo-makeの導入"><a href="#cargo-make%E3%81%AE%E5%B0%8E%E5%85%A5">cargo-makeの導入</a></h3> <p>cargo-makeの実行にはcargo-makeバイナリが必要になるのでインストールしておきます.</p> <pre><code class="bash">cargo install --force cargo-make </code></pre> <p>次にプロジェクトを作成します. これもコマンド一つでできます. 今回はwasmモジュールとして読み込まれるので--libオプションをつけます.</p> <pre><code class="bash">cargo new --lib project-name </code></pre> <p>できたらプロジェクト・ルートに移動し実行してみましょう.</p> <pre><code class="bash">cago make </code></pre> <p>この時点では<a target="_blank" rel="nofollow noopener" href="https://github.com/sagiegurari/cargo-make#default-tasks-and-extending">デフォルトのtomファイル</a>が参照されます. 次にMakefile.tomlファイルを作成します.</p> <pre><code class="bash">cd project-name touch Makefile.toml </code></pre> <p>同様に実行すると今度はMakefile.tomlをもとに実行が行われます. 任意のmakefileを指定するには--makefileオプションを使います.</p> <pre><code class="bash">cargo make --makefile ./my_build.toml test </code></pre> <h3 id="Makefile.tomlの書き方"><a href="#Makefile.toml%E3%81%AE%E6%9B%B8%E3%81%8D%E6%96%B9">Makefile.tomlの書き方</a></h3> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/seed-rs/seed-quickstart/blob/master/Makefile.toml">seed-quickstart/Makefile.toml</a>の解説です. 必要ない方は飛ばしましょう.</p> <h4 id="タスク"><a href="#%E3%82%BF%E3%82%B9%E3%82%AF">タスク</a></h4> <p>実行するコマンドはタスクという単位で管理します. 何もしないタスクは以下のようになります.</p> <pre><code class="toml">[tasks.do_nothing] # do nothing </code></pre> <h4 id="基本"><a href="#%E5%9F%BA%E6%9C%AC">基本</a></h4> <p>cargoのbuildサブコマンドを呼び出してみましょう.</p> <pre><code class="toml"># cargo make compile [tasks.compile] description = "Build" workspace = false command = "cargo" args = ["build"] </code></pre> <p>それぞれの意味は以下のようになります.</p> <div class="table-responsive"><table> <thead> <tr> <th>セクション</th> <th>意味</th> </tr> </thead> <tbody> <tr> <td>description</td> <td>このタスクの内容</td> </tr> <tr> <td>workspace</td> <td>workspaceでタスクを実行するかどうか</td> </tr> <tr> <td>command</td> <td>実行するメイン・コマンド</td> </tr> <tr> <td>args</td> <td>引数の指定</td> </tr> </tbody> </table></div> <h4 id="依存タスク"><a href="#%E4%BE%9D%E5%AD%98%E3%82%BF%E3%82%B9%E3%82%AF">依存タスク</a></h4> <p>dependencies属性を指定するとコマンドの依存性を指定できます. 要するに呼び出し順序です.</p> <pre><code class="toml"># cargo make start [tasks.start] description = "Combine the build and serve tasks" workspace = false dependencies = ["build"] </code></pre> <p>これでcargo startを実行するとbuildタスクが実行されます.</p> <h4 id="開発サーバーと環境変数"><a href="#%E9%96%8B%E7%99%BA%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E3%81%A8%E7%92%B0%E5%A2%83%E5%A4%89%E6%95%B0">開発サーバーと環境変数</a></h4> <p>開発サーバーとしてmicroserverというクレートを使います. 簡単なWeb UIの開発には便利そうなのとクレート導入の例として紹介しておきます. 開発の趣旨を開発者の人がブログに書いています.</p> <p><a target="_blank" rel="nofollow noopener" href="https://robertohuertas.com/2018/11/01/microserver/">Microserver: local http server with SPA support</a></p> <p>今回のようにあるタスクの前提となるバイナリ・クレートのインストールも記述できます.</p> <pre><code class="toml">[tasks.serve] description = "Start server" install_crate = { crate_name = "microserver", binary = "microserver", test_arg = "-h" } workspace = false command = "microserver" args = ["--port", "${PORT}"] </code></pre> <p>サーバーということでポートの指定もしています. 環境変数もenvセクションで指定できます.</p> <pre><code class="toml">[env] PORT = "8000" </code></pre> <p>別ファイルに指定して読み込むこともできます.</p> <pre><code class="toml">[env] env_files = [ "./my_env.env", ] </code></pre> <h4 id="conditionによる条件設"><a href="#condition%E3%81%AB%E3%82%88%E3%82%8B%E6%9D%A1%E4%BB%B6%E8%A8%AD">conditionによる条件設</a></h4> <p>ある条件を満たすときにタスクを実行することもできます. 環境変数がきちんと指定されている場合だけ実行するという条件ならconditionセクションをタスクに追加します.</p> <pre><code class="toml">[tasks.start] condition = { env_set = [ "PORT" ] } </code></pre> <p>あるいは特定の環境変数を条件にして新しい変数を定義することができる.</p> <pre><code class="toml">[env] PORT_EXISTING = { value = "true", condtion = { env_set = ["PORT"] } } PORT = { value = "8000", condition = { env_not_set = ["PORT"] } } </code></pre> <p>条件によって読みやすい環境変数に変換したり, 環境変数が定義されていない場合に設定したりということができそうです.</p> <h4 id="profileによるモードの切り替え"><a href="#profile%E3%81%AB%E3%82%88%E3%82%8B%E3%83%A2%E3%83%BC%E3%83%89%E3%81%AE%E5%88%87%E3%82%8A%E6%9B%BF%E3%81%88">profileによるモードの切り替え</a></h4> <p>webpackのモードの指定ののようなこともできます.</p> <pre><code class="toml">[env] env_files = [ { path = "./development.env", profile = development }, { path = "./production.env", profile = "production } ] </code></pre> <pre><code class="bash">cargo make --profile production some_task </code></pre> <p>developmentはデフォルト値なので指定する必要はないです.</p> <h4 id="タスクの拡張とリリース・ビルド"><a href="#%E3%82%BF%E3%82%B9%E3%82%AF%E3%81%AE%E6%8B%A1%E5%BC%B5%E3%81%A8%E3%83%AA%E3%83%AA%E3%83%BC%E3%82%B9%E3%83%BB%E3%83%93%E3%83%AB%E3%83%89">タスクの拡張とリリース・ビルド</a></h4> <p>タスク名をextend属性で指定するとタスクを拡張できます. 例えばcompileタスクをリリース・モードでビルドするように拡張すると以下のようになります.</p> <pre><code class="toml">[tasks.compile_release] description = "Release Build " extend = "compile" args = ["build", "--release"] </code></pre> <p>プラットフォームごとの拡張も簡単にできます.</p> <pre><code class="toml">[tasks.hello-world] script = [ "echo \"Hello World From Unknown\"" ] [tasks.hello-world.linux] script = [ "echo \"Hello World From Linux\"" ] [tasks.hello-world.mac] script = [ "echo \"Hello World From macOS\"" ] </code></pre> <h4 id="スクリプティング"><a href="#%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0">スクリプティング</a></h4> <p>シェルスクリプトを指定して実行することもできます.</p> <pre><code class="toml">[tasks.echo] script = [ "echo hello world" ] </code></pre> <p>script_runner属性を指定することでpythonなどスクリプトのランナーを指定できます.</p> <pre><code class="toml">[tasks.python] script_runner = "python" script_extension = "py" script = [ ''' print("Hello, World!") ''' ] </code></pre> <p>またファイルを指定して実行することもできます.</p> <pre><code class="toml">[tasks.run_from_script] script = { file = "hello.py" } </code></pre> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/sagiegurari/cargo-make#rust-code">@rust</a>の指定でRustを実行することもできます.</p> <h4 id="run_tasks"><a href="#run_tasks">run_tasks</a></h4> <p>実行するタスクを指定します. dependenciesで指定したタスクは事前に実行されますが, run_task属性で指定したタスクは事後に実行されます.</p> <pre><code class="toml">[tasks.pre_task] script = [ "echo pre task"] [tasks.post_task] script = [ "echo post task"] [tasks.do_something] dependencies = ["pre_task"] run_task = "post_task" </code></pre> <p>この例の場合pre_task -> do_something -> post_taskの順で実行されます. 並列実行やフォークなど細かいタスクのフローを設定することできます. 詳しくは<a target="_blank" rel="nofollow noopener" href="https://github.com/sagiegurari/cargo-make#sub-task">Sub Task</a>を参照してください.</p> <h4 id="エイリアス"><a href="#%E3%82%A8%E3%82%A4%E3%83%AA%E3%82%A2%E3%82%B9">エイリアス</a></h4> <p>タスクを別名で参照できます.</p> <pre><code class="toml">[tasks.build] alias = "default_build" </code></pre> <p>Seedの<a target="_blank" rel="nofollow noopener" href="https://github.com/seed-rs/seed/tree/master/examples">examples</a>フォルダは複数のクレートが含まれており, そこにはMakefile.tomlが存在します. それぞれのクレートではプロジェクト・ルートのMakefile.tomlを拡張する形でルートのタスクを参照しています. つまり実行する処理は同じといことです.</p> <h4 id="条件付き実行"><a href="#%E6%9D%A1%E4%BB%B6%E4%BB%98%E3%81%8D%E5%AE%9F%E8%A1%8C">条件付き実行</a></h4> <p>条件を満たした場合にタスクが実行されます.</p> <pre><code class="toml">[tasks.test-condition] condition = { platforms = ["windows", "linux"], channels = ["beta", "nightly"], profiles = ["development", "production"], rust_version = { min = "1.39.0", max = "1.42.0" } } script = [ "echo \"condition was met\"" ] </code></pre> <p>このタスクはmacでstableチャネルを利用している人は実行されません. またscriptの代わりにrun_taskで他のタスクを条件を満たした時だけ実行するということもできます.</p> <h4 id="watchモード"><a href="#watch%E3%83%A2%E3%83%BC%E3%83%89">watchモード</a></h4> <p>watch属性をつけるとwatchモードで実行できます.</p> <pre><code class="toml">[tasks.run_from_script] script = { file = "hello.py" } watch = true </code></pre> <p>監視対象からの除外のような設定もできます.</p> <pre><code class="toml">[tasks.watch] description = "Start building project in watch mode" workspace = false dependencies = ["build", "build_wasm"] watch = { ignore_pattern="pkg/*" } </code></pre> <p>watchモードでサーバーを起動することはできません. この場合run_taskのparallelを使うとファイルの変更を監視しながら配信もで可能です.</p> <pre><code class="toml">[tasks.dev] description = "Build in watch mode while serving file" run_task = [ { name = ["watch", "serve"], parallel = true } ] </code></pre> <h3 id="tailwindcssの導入"><a href="#tailwindcss%E3%81%AE%E5%B0%8E%E5%85%A5">tailwindcssの導入</a></h3> <p>npmを使います.</p> <pre><code class="bash">npm init # if needed npm install tailwindcss </code></pre> <p>これでtailwindというコマンドがパッケージ上で使えるようになります. cssフォルダを作成して以下の内容をstyle.cssファイルを新規作成します.</p> <pre><code class="css">@tailwind base; @tailwind components; @tailwind utilities; </code></pre> <p>これをビルドして利用します. publicフォルダを同じ階層に作っておいて, css用のフォルダを作ります. carg-makeのタスクを追加しましょう.</p> <pre><code class="toml"># cargo make tailwind [tasks.tailwind] script = [ "npx tailwind build ./css/style.css -o ./public/css/style.css", ] </code></pre> <p>style.cssからstyle.cssが出力されますが中身を見ると見れ慣れたCSSファイルです. 出力されたファイルをpublic/index.htmlに読み込めばtailwindが提供するユーティリティ・クラスを利用できます.</p> <p>Seedでtailwindを使ってみましょう. Seedの説明はしませんがRustのマクロを使って要素を記述できます. 注目するのはclassマクロです. ここに指定された文字列がtailwindcssのユーティリティ・クラス名です.</p> <pre><code class="rust">fn view(model: &Model) -> impl View<Msg> { let button_class = class!["bg-gray-400", "px-8", "py-4"]; div![ class![ "flex", "flex-col", "justify-center", "items-center", "h-screen", "text-gray-600" ], button![ button_class, simple_ev(Ev::Click, Msg::Increment), format!("Click Me!") ], div![ class!["w-56", "text-center", "mt-2"], format!("Click {} times", model.counter) ] ] } </code></pre> <p>こんな感じの表示になりちゃんと表示されました(クリック時にカウント数を表示するラベルが動くバグがありますが・・・)</p> <p><a href="https://crieit.now.sh/upload_images/06db047230e8021afca815dae9b4f1595ea58291385eb.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/06db047230e8021afca815dae9b4f1595ea58291385eb.png?mw=700" alt="Seed with tailwindcss" /></a></p> <h2 id="まとめ"><a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></h2> <p>もうちょっとまとまりがあれば良いと思ったのですが, 意外と機能が多く詳細は公式の<a target="_blank" rel="nofollow noopener" href="https://github.com/sagiegurari/cargo-make">README.md</a>を読むのがいいと思います. <a target="_blank" rel="nofollow noopener" href="https://github.com/sagiegurari/cargo-make/tree/master/examples">examplesフォルダ</a>に例が豊富なので参考になると思います.</p> <p>tailwindcssはかなり使いやすいですしSeedもいい感じです(ただビルドが遅いですが・・・).</p> <h2 id="補足"><a href="#%E8%A3%9C%E8%B6%B3">補足</a></h2> <h3 id="WorkspaceとWorkspace Flow"><a href="#Workspace%E3%81%A8Workspace+Flow">WorkspaceとWorkspace Flow</a></h3> <p>タスクにworkspace属性を指定できました. Workspaceとは何でしょうか?</p> <blockquote> <p>A workspace is a set of packages that share the same Cargo.lock and output directory.</p> </blockquote> <p><a target="_blank" rel="nofollow noopener" href="https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html#cargo-workspaces">Cargo Workspaces - The Rust Programming Language</a></p> <p>要するに複数のパッケージを一つにまとめたものです. ただし単一のプロジェクトとして管理される前提なので最終生成物やCargo.lockなどで全体のクレートのバージョンなどは共通化されています. 実態は以下のような内容のCargo.tomlとmembers属性で指定されたメンバーとなるパッケージが存在するフォルダです.</p> <pre><code class="toml">[workspace] members = [ "client", "server", ] </code></pre> <p>こうした構成はSeedの<a target="_blank" rel="nofollow noopener" href="https://github.com/seed-rs/seed/tree/master/examples/server_integration">server_integration</a>という例が参考になると思います. 例えば適当なウォークスペースにmakefileを作りworkspace属性を指定します.</p> <pre><code class="toml">[tasks.do_something] workspace = false </code></pre> <p>なぜこのような設定が必要なのでしょうか. 通常cargo-makeのタスクはworkspace直下では実行されません. タスクの要求はメンバー・クレートで実行されます(workspace flow). このおかげでウォークスペースで実行したビルド処理が各クレートで実行されることになり, 共通の処理をウォークスペースにまとめられるので構成ファイルを小さくできます.</p> <p>しかしウォークスペースで実行したい場合もあるでしょう. その場合にこの機能をオフにするのがworkspace属性の意味です. この値はデフォルトでtrueになっています.</p> <pre><code class="toml">[config] default_to_workspace = false </code></pre> <p>のようにも指定するとデフォルト値をfalseに上書きできます.</p> <p>あるいはコマンド実行時にオプションとして渡すこともできます.</p> <pre><code class="bash">cargo make --no-workspace mytask </code></pre> <h3 id="CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILEフラグ"><a href="#CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE%E3%83%95%E3%83%A9%E3%82%B0">CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILEフラグ</a></h3> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/sagiegurari/cargo-make#automatically-extend-workspace-makefile">CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILEフラグ</a>はウォークスペース直下のmakefileに指定します. そうすると自動的に個々のメンバー・クーレトが持つmakefileはルートのmakefileをextendで読み込み参照できるようになるようです.</p> <h3 id="WASM in A Nutshell"><a href="#WASM+in+A+Nutshell">WASM in A Nutshell</a></h3> <p>WebAssemblyという技術の略称がWASMでコードの拡張子にもなっている. 通常ブラウザはJavaScriptのランタイムを備えており(V8やスパイダーモンキー)JavaScriptのみを実行できる. JIT(Just In Time)コンパイラによる最適化など高速化されたが, 原理的にはランタイムはJavaScriptを逐次解釈してマシンコードに翻訳しそれを実行するために遅い. このプロセスを飛ばせれば, ネットーワークにるRTT(Round-Trip Time)を無視すればネイティブ並みに高速化できるわけです. これはPythonやRubyなどのインタプリタ言語がC/C++やRustなどの言語より遅い事と基本的には同じ関係と言えそうです.</p> <p>そこでWeb版のアセンブラを導入しようという話になるわけです. 通常アセンブリ言語はマシン語と1対1に対応するニーモニックを用いて表現されますが, WASMがターゲットとするのは複数のマシンを抽象化したマシンになります.</p> <blockquote> <p>So WebAssembly is a little bit different than other kinds of assembly. It’s a machine language for a conceptual machine, not an actual, physical machine.</p> </blockquote> <p><img src="https://2r4s9p1yi1fa2jd7j43zph8r-wpengine.netdna-ssl.com/files/2017/02/04-03-toolchain07.png" alt="image" /></p> <p><a target="_blank" rel="nofollow noopener" href="https://hacks.mozilla.org/2017/02/creating-and-working-with-webassembly-modules/">Creating and working with WebAssembly modules</a></p> <p>この説明を聞くとJavaに近い感じを受ける. 実際に(この比較はおかしいけど)WASIとJavaの類似性を指摘した記事なんかもある.</p> <p><a target="_blank" rel="nofollow noopener" href="https://www.infoq.com/jp/news/2019/05/wasi-wasm-system-interface/">MozillaがWASIイニシアティブを発表、WebAssemblyをすべてのデバイス、コンピュータ、オペレーティングシステムで動作可能に</a></p> <p>また公式ではWASMを以下のように定義している.</p> <blockquote> <p>WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine.</p> </blockquote> <p><a target="_blank" rel="nofollow noopener" href="https://webassembly.org/">WebAssembly</a></p> <p>スタック・マシンは良くわからないけどWikipediaによると<a target="_blank" rel="nofollow noopener" href="https://ja.wikipedia.org/wiki/Java仮想マシン">Java仮想マシン</a>も似たような定義で紹介されている.</p> <blockquote> <p>Java仮想マシン (Java virtual machine、Java VM、JVM) は、Javaバイトコードとして定義された命令セットを実行するスタック型の仮想マシン。</p> </blockquote> <h2 id="Reference"><a href="#Reference">Reference</a></h2> <ol> <li><a target="_blank" rel="nofollow noopener" href="https://doc.rust-lang.org/cargo/">The Cargo Book</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/sagiegurari/cargo-make#usage-watch">cargo-make</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/seed-rs/seed-quickstart/blob/master/Makefile.toml">Makefile.toml - seed-quickstart</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://seed-rs.org/guide/view">View - Seed</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://tailwindcss.com/docs/installation">Installation - tailwindcss</a></li> </ol> <h2 id="例題"><a href="#%E4%BE%8B%E9%A1%8C">例題</a></h2> <p>タスクの依存関係, watchモードやcrateの導入などcargo-makeの基本的な使い方を学べる.</p> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/seed-rs/seed-quickstart">seed-quickstart</a></p> <p>examplesフォルダからルート・フォルダにあるMakefile.tomlの参照法などが参考になりました.</p> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/seed-rs/seed/tree/master/examples">examples -seed</a></p> <h2 id="課題"><a href="#%E8%AA%B2%E9%A1%8C">課題</a></h2> <p>SeedのようなWebフロントエンドの開発では, プロジェクトをcargoパッケージとしてマインに構成するのかnpmパッケージとしてメインに構成するのかが問題になる. cargo-makeがない場合はnpmパッケージ以下にcargoパッケージを作らないとビルド・プロセスが自動にできない. seed-quickstart-webpackもwebpackを使ってrustライブラリのビルドからwasmモジュールの読み込みなどを行なっている. これをcargo-makeベースに置き換えたい. npm-scriptでコマンド化しておけば, cargo-makeから呼び出せる.</p> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/seed-rs/seed-quickstart-webpack">seed-quickstart-webpack</a></p> <h2 id="Further Reading"><a href="#Further+Reading">Further Reading</a></h2> <p><a target="_blank" rel="nofollow noopener" href="https://hacks.mozilla.org/2017/02/a-crash-course-in-assembly/">A crash course in assembly</a><br /> <a target="_blank" rel="nofollow noopener" href="https://hacks.mozilla.org/2017/02/a-cartoon-intro-to-webassembly/">A cartoon intro to WebAssembly</a><br /> <a target="_blank" rel="nofollow noopener" href="https://hacks.mozilla.org/2017/02/what-makes-webassembly-fast/">What makes WebAssembly fast?</a></p> ブレイン