Madogiwa Blog

主に技術系の学習メモに使っていきます。

esbuild-loaderを使ってwebpackの一部loaderをesbuildに差し替えて高速化するメモ📝

webpackの移行先として、下記の図の通り速度を重視しesbuildが検討されることも多いかと思いますが、

esbuild - An extremely fast JavaScript bundler

webpackを使いつつ一部のbuildをesbuildに置き換えるようなことができるesbuild-loaderというライブラリがあるようなので試してみました。

github.com

前提

いかのようにjsをbabel-loader、tsをts-loaderでbuildするような構成のものを、開発環境でだけ、それぞれesbuildでbuildするようにしてみます。 ※環境別のconfigの切り替えにはwebpack-mergeを利用しています

module: {
    rules: [
      {
        test: /\.(scss|css)/,
        use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
        },
      },
      {
        test: /\.ts$/,
        exclude: /node_modules/,
        loader: "ts-loader",
        options: {
          // /vueをtypescriptとして扱う
          appendTsSuffixTo: [/\.vue/],
          transpileOnly: true,
        },
      },
      {
        test: /\.vue/,
        loader: "vue-loader",
        options: {
          loaders: {
            scss: "vue-style-loader!css-loader!sass-loader",
          },
        },
      },
      {
        test: /\.(png|jpe?g|gif)$/i,
        type: "asset/resource",
      },
    ],
  },

webpack configを修正してesbuildに置き換える

以下のようにbabel-loaderとts-loaderをesbuildでのbuild設定で上書きしてあげるだけで大丈夫でした!

process.env.NODE_ENV = "development";

const { merge } = require("webpack-merge");
const common = require("../../webpack.common.js");
const babelLoaderIndex = common.module.rules.findIndex((rule) => rule.use?.loader == "babel-loader");
const tsLoaderIndex = common.module.rules.findIndex((rule) => rule.use?.loader == "ts-loader");

common.module.rules[babelLoaderIndex] = {
  test: /\.js$/,
  exclude: /node_modules/,
  use: {
    loader: "esbuild-loader",
    options: { loader: "js", target: "es2015", sourcemap: true },
  },
};

common.module.rules[tsLoaderIndex] = {
  test: /\.ts$/,
  exclude: /node_modules/,
  use: {
    loader: "esbuild-loader",
    options: { loader: "ts", target: "es2015", sourcemap: true },
  },
};

module.exports = merge(common, {
  mode: process.env.NODE_ENV,
});

改善結果

個人のwebサービス(対象ファイル70ファイル程度)を試しにesbuild-loaderに差し替えてみたところ0.5秒ほど早くなりました🚀 この程度のファイル数でも速度が変わったので大規模サービスの場合には、より明確な差が出そう。

// ts-loader and babel-loader: 最速・最遅を除いた平均 3.953
✨  Done in 3.69s.
✨  Done in 3.85s.
✨  Done in 3.93s.
✨  Done in 4.08s.
✨  Done in 4.31s.


// esbuild-loader: 最速・最遅を除いた平均 3.4833
✨  Done in 3.38s.
✨  Done in 3.47s.
✨  Done in 3.49s.
✨  Done in 3.49s.
✨  Done in 3.59s.

esbuild-loaderはwebpack v4でもテスト実行してるので動きそうですね。

https://github.com/privatenumber/esbuild-loader/blob/v2.19.0/test/loader.test.ts#L12-L15

おわりに

webpackをesbuildに移行せずに一部差し替えてお手軽に高速化できるのは良いですね✨

参考

buildersbox.corp-sansan.com

`eslint-plugin-vue`を使って指定行以上のVue SFC(.vue)をエラーにして肥大化を防ぐメモ

Vue SFC(.vue)を使ってコンポーネントに分割し疎結合にすることでコードの見通しを良くし負債化を抑制できますが、適切なタイミングで分割できずコンポーネントが大きくなりすぎると見通しが悪くなり負債になってしまうので行数とかでエラーにできないかなと思って調べていたところeslint-plugin-vueで簡単に検知できそうだったのでメモ📝

www.npmjs.com

やり方は以下のようにvue/max-lenの設定をruleに入れて上げるだけです。

{
  ...
  "rules": {
    "vue/max-len": ["error", { "code": 150 }],
    ]
  },
}

上記の設定だと150行を超えるとeslint実行時にエラーとなります。

他にもtemplateやscriptだけ行数を制限したりできるようです(便利✨ 詳しくは以下の公式ドキュメントをご確認ください。

eslint.vuejs.org

意外とplugin:vue/vue3-recommendedでデフォルトで設定されてないけど設定しておくと良さそうなRule結構ありそう。

追記(2023/09/24)

以下で公式でtemplate,script,styleの行数を指定できるruleが追加された 🎉

eslint.vuejs.org

特定ディレクトリ以下の全ファイルのchecksum(MD5)を取得する方法MEMO

バージョンアップによるbuild結果の差分を見るときなど特定のディレクトリ内の全ファイルを md5でchecksumを取得して差分が出てるファイルを特定できると便利なのでやり方をメモ🗒

mac

ls -Fd $(find $FIND_PATH)で特定ディレクトリ以下のファイル + ディレクトリを取得し、 grep -v "/$"ディレクトリを除外、xargs md5でchecksumを取得します。

ファイル名 + checksum

FIND_PATH=$PWD
ls -Fd $(find $FIND_PATH) | grep -v "/$" | xargs md5

出力例

MD5 (/test/file1.txt) = b026324c6904b2a9cb4b88d6d61c81d1
MD5 (/test/file2.txt) = 26ab0db90d72e28ad0ba1e22ee510510

checksumのみ

checksumのみの場合は-qオプションを使うと楽です。

FIND_PATH=$PWD
ls -Fd $(find $FIND_PATH) | grep -v "/$" | xargs md5 -q

出力例

b026324c6904b2a9cb4b88d6d61c81d1
26ab0db90d72e28ad0ba1e22ee510510

ubuntu

基本的にはmacと同じ、ubuntsuの場合にはmd5sumを使う。

ファイル名 + checksum

ls -Fd $(find public/packs) | grep -v "/$" | xargs md5sum

checksumのみ

md5sumには-qオプションが無いのでawk '{ print $1 }'で空白区切りの最初の文字(md5 checksum)を取得する。

ls -Fd $(find public/packs) | grep -v "/$" | xargs md5sum | awk '{ print $1 }'

参考

qiita.com

uxmilk.jp

qiita.com

www.yamacoco.com

モジュラモノリスの概要とRuby on Railsにおける実装パターンについてのMEMO

Railsを活用したWebサービスが大規模になってきた場合に、最近モジュラモノリスが採用されるのを良く耳にする気がしますが、現状その概要やRailsにおいてどのように実装されているのか、あまり分かっていなかったのでメモ🗒

モジュラモノリスとは?

モジュラーモノリス とは、単一プロセスが別々のモジュールで構成され、それぞれ独立して作業できるものの、デプロ イのために結合する必要があるシステムだ。
Sam Newman著、島田 浩二訳 (2020年12月発行) モノリスからマイクロサービスへ ――モノリスを進化させる実践移行ガイド オライリー・ジャパン

モジュラモノリスとは上記の通り、単一プロセスで動作するアプリケーションを一定のモジュール単位に分割し、それらの依存を分離することでモジュール単位での開発を実現するアーキテクチャのようです。

マイクロサービスとの比較

Webサービスが大規模になてきた場合に採用されるアーキテクチャとしてマイクロサービスがあります。
マイクローサービスを採用することにより、独立したデプロイや柔軟な技術選択を実現できる一方、下記のようなサービス間の通信によるレイテンシの悪化やサービスを跨いだ開発コストの増加といったリスクもあります。

ネットワーク越しのコンピューター間通信は、瞬時には行われない。これは、レイテンシー、 とりわけローカル内の処理をはるかに上回るレイテンシーを気にする必要があることを意味する。 レイテンシーが変動し、システムの動作を予測不可能にすることがあるのを考えると、事態はさら 悪化する。また、ネットワーク障害によってパケットが失われたり、ネットワークケーブルが切 断されたりするという事象にも対処する必要がある。
Sam Newman著、島田 浩二訳 (2020年12月発行) モノリスからマイクロサービスへ ――モノリスを進化させる実践移行ガイド オライリー・ジャパン


複数のサービスにわたる大規模なリファクタリングは面倒で、依存するすべてのサービスにおける変更やデプロイの調整も必要になります。
[翻訳] Shopifyにおけるモジュラモノリスへの移行 - Qiita

モジュラモノリスは、任意のコンテキストで別のサービスではなくモジュールに切り出し、それらの依存性を分離し境界を設けることによって、 各モジュールへの認知負荷を下げるといったマイクロサービスで得られるメリットの一部を享受しつつ、レイテンシといったデメリットを抑制したアーキテクチャパターンだと思いました。

[翻訳] Shopifyにおけるモジュラモノリスへの移行 - Qiita

Railsにおける実装パターン

Ruby on Railsにおいてモジュラモノリスアーキテクチャに関する記事を記載します。(見つけ次第追記)

Shopify

shopify.engineering

qiita.com

メモ

  • ディレクトリ、moduleといった言語レベルでシンプルに境界を表現しているっぽい。依存の分離を行うための仕組みは別途用意して運用している模様。
  • Railsのルートディレクト直下に各種分割されたモジュール単位で app 配下(assets, controller, model, view 等)及び test を持つような構成を取っている模様。
  • Gemfileとかdatabaseは共有してそうだった。

Rails Engine

medium.com

blog.kymmt.com

メモ

  • Railsエンジンを使ってモジュール分割するパターン、Railsエンジンを使うと単体での動作確認とか依存性の分離とかはしやすそう。
  • RailsエンジンなのでGemfileとかもモジュール毎に分離しやすそう。
  • Railsエンジンを採用したとしてもフロントエンドまわりはbuild結果をルートのRailsアプリのpublic配下にモジュール単位でディレクト切って、そこに配置するみたいなことは検討しないとダメそう

おわりに

モジュラモノリス、あまり今まで実態がよく分かっていなかったのですが調べてみると、やり方はいろいろあれどモノリスなWebアプリケーションを適切なコンテキストの元分割し依存性を分離しましょうということだと理解した。 いずれマイクロサービスにするにしろ依存性を分離することは必要なので、モノリスからの移行先としてモジュラモノリスは有用そうに思った。

参考資料

r-kaga.com

tech-blog.rakus.co.jp

GitHub Actionの`on: pull_request`と`on: pull_request_target`の違いMEMO

最近GitHub Actionを触っていて、タイトル通りon: pull_requeston: pull_requestの実行コンテキスト等の違いによって、 大分ハマったので、それぞれの違いとかを調べてみたのでMEMOしておきます。

https://github.com/features/actions

on: pull_requeston: pull_request_targetの違い

基本的には実行コンテキストと与えられる権限が違うようです。

GitHub Actionの実行コンテキストとか権限まわり難しい。。。

参考

pull_request_target トリガーを使って fork されたリポジトリからの pull request で発火した GitHub Actions で fork 元の secrets を参照する - make clean; make

securitylab.github.com


Ruby: Bundlerを使って1つのRubyのファイルだけでgemをinstallして利用する方法

bundler/inlineを使うとGemのバグ報告やlogger系のGemを使った調査などでGemfileを使わずに1つのRubyファイルだけで完結できると便利だったのでMEMO

bundler.io

例えば調査のために以下のpretty print系のGemを使いたいとします。

github.com

普通だと以下のGemfileを作成しbundle installしてRubyファイル内で利用の流れになりますが、

# Gemfile

gem "spp"
# sample.rb
Spp::spp "Hello! Bundler inline!!"
$ ruby sample.rb
========== START ==========
"Hello! Bundler inline!!"
========== E N D ==========

bundler/inlineを使うと以下のように1つのRubyファイルで完結して記述できて便利です✨

require 'bundler/inline'

gemfile do
  source 'https://rubygems.org'
  gem 'spp'
end

Spp::spp "Hello! Bundler inline!!"
$ ruby sample.rb
========== START ==========
"Hello! Bundler inline!!"
========== E N D ==========

以下のような感じでGitHubから取得することもできそうだった、便利!

require 'bundler/inline'

gemfile do
  source 'https://rubygems.org'
  gem 'spp', :git => 'https://github.com/madogiwa0124/spp'
end

Spp::spp "Hello! Bundler inline!!"

Railsの再現コードのbug reportのtemplateでも利用されているみたいですね!

github.com

おわり

Vue.js: `vue-tsc`でSFC内のtemplateも含めてTypeScriptの型チェックを行うMEMO

vue-tscなるものを使うとSFC内のtemplate部分も含めて型チェックを行えて便利っぽいようなので使い方とかをメモ

www.npmjs.com

vue-tscとは?

vue-tscとはVeturの後継であるVolar内で管理されているライブラリです。

github.com

vue-tsc Type-check and dts build command line tool

上記の通りSFC内のテンプレートを含めた型チェックを行うことができます。

vue-tscの使い方

インストール

以下でinstallします📦

# yarn
yarn add -D vue-tsc

# npm
npm i vue-tsc -D

tsconfig.jsonの設定

以下のVue.jsの公式ドキュメントに記載されている推奨設定をもとにtsconfig.jsonを修正します。

v3.ja.vuejs.org

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "strict": true,
    "jsx": "preserve",
    "moduleResolution": "node"
  }
}

package.jsonのコマンド

元々TypeScriptでの型チェックを行なっていたコマンドにvue-tsc --noEmitを追記します。

"lint:type-check": "tsc -p . --noEmit && vue-tsc --noEmit",

型チェックの実行

実行すると以下のような形でtemplate部分の型チェック結果が表示されます🙆✨

$ yarn lint:type-check

app/javascript/components/entry/EntryCardCollection.vue:3:32 - error TS2339: Property 'entries' does not exist on type 'never'.

3     <div v-for="entry in props.entries" :key="entry.id" :class="`column is-${clumnSize}`">

VS Code上で警告出す

以下の拡張機能を使うとVS Code上で警告を出せるのでより便利でした。

marketplace.visualstudio.com

※Veturと競合するようなので、Veturは無効化しておくのが良いようです。

おわりに

template部分も型チェックできるの、大分良いですね!!

参考

tech.visasq.com

zenn.dev