איך עובד פרויקט Rails עם ESBuild
החלום של DHH עבור ריילס הוא לוותר על ה Build Step ופשוט לשלב בדף ה HTML את הקישורים לסקריפטים באמצעות ES Modules. בזכות התמיכה של דפדפנים ב HTTP2 וב Import maps הסיפור הזה אפילו עובד די טוב בפרויקטי ריילס המסתמכים על JavaScript.
אבל כשאנחנו מוסיפים TypeScript וריאקט לתמונה העסק מסתבך ושלב בניה הופך הכרחי. גירסה 7 של ריילס שילבה ממשק סטנדרטי לבניית קבצי Front End שנקרא JS Bundling. מנגנון זה מאפשר לעבוד עם esbuild, webpack או rollup ולשלב אותם בתוך פרויקט ריילס. אבל מה שמיוחד ב JS Bundling בעולם של ריילס הוא שאין קובץ הגדרות אחד שעובד על כל הכלים ובמקום זה עלינו לבחור את אחד מהכלים ולבנות לו את קובץ ההגדרות בעצמנו. לפני כמה שבועות פרסמתי כאן מדריך שמסביר איך לשלב React ו TypeScript בפרויקט ריילס ושם הצעתי על הספריה vite-ruby. היום אני רוצה להראות מבנה פרויקט דומה אבל הפעם בעזרת esbuild ותוך שימוש ב JSBundling.
1. יצירת הפרויקט והגדרות
תחילה אני יוצר פרויקט ריילס חדש עם הפקודה:
$ rails new myapp -j esbuild
הפרויקט שנוצר כולל קובץ package.json עם הגדרת סקריפט לבניה:
"scripts": {
"build": "esbuild app/javascript/*.* --bundle --sourcemap --format=esm --outdir=app/assets/builds --public-path=/assets"
},
בשביל להפעיל את הפרויקט משורת הפקודה אנחנו כותבים:
./bin/dev
זה גורם להפעלת הפקודות מתוך הקובץ Procfile שגם נוצר באותה תיקייה:
web: env RUBY_DEBUG_OPEN=true bin/rails server
js: yarn build --watch
פקודת ה js שם מפעילה את סקריפט הבנייה ומוסיפה לו את המתג watch. בצורה כזאת התהליך של js ימשיך לרוץ ולבנות מחדש את קבצי ה JavaScript כל פעם שיהיה שינוי בקבצים בעזרת esbuild. כשנרצה לבנות את הפרויקט לפרודקשן נפעיל:
./bin/rails assets:precompile
וזה יריץ אוטומטית את סקריפט ה build מקובץ ה package.json. בגלל פקודת הבניה שמוגדרת שם ESBuild יבנה את קבצי ה JavaScript וישמור אותם לתוך תיקיית app/assets/build
. שם יאסוף אותם sprockets בגלל קובץ ההגדרות app/assets/config/manifest.js
. זה תוכנו:
//= link_tree ../images
//= link_directory ../stylesheets .css
//= link_tree ../builds
בעזרת sprockets הקבצים יועתקו לתיקיית public וגם יקבלו תוספת של digest לשמם. אחרי בנייה אני מוצא את הקבצים הבאים שם:
$ ls public/assets/*.js
public/assets/actioncable-1c7f008c6deb7b55c6878be38700ff6bf56b75444a086fa1f46e3b781365a3ea.js
public/assets/actioncable.esm-06609b0ecaffe2ab952021b9c8df8b6c68f65fc23bee728fc678a2605e1ce132.js
public/assets/actiontext-78de0ebeae470799f9ec25fd0e20ae2d931df88c2ff9315918d1054a2fca2596.js
public/assets/actiontext.esm-328ef022563f73c1b9b45ace742bd21330da0f6bd6c1c96d352d52fc8b8857e5.js
public/assets/activestorage-503a4fe23aabfbcb752dad255f01835904e6961d5f20d1de13987a691c27d9cd.js
public/assets/activestorage.esm-b3f7f0a5ef90530b509c5e681c4b3ef5d5046851e5b70d57fdb45e32b039c883.js
public/assets/application-c3ea1831495badf025afd8fd7722fb857c51927f0cc017c366fda52f0d926be2.js
public/assets/common-4e2a87d110376bedbe6f1edea889eb9c368a64a988cab5d3d8527dafd52dc067.js
public/assets/hello-7301560822385b452fd44614e6fc857b520f5082b343eed0dea9d5d2812db9e4.js
public/assets/manifest-dad05bf766af0fe3d79dd746db3c1361c0583026cdf35d6a2921bccaea835331.js
public/assets/stimulus-autoloader-c584942b568ba74879da31c7c3d51366737bacaf6fbae659383c0a5653685693.js
public/assets/stimulus-f75215805563870a61ee9dc5a207ce46d4675c7e667558a54344fd1e7baa697f.js
public/assets/stimulus-importmap-autoloader-db2076c783bf2dbee1226e2add52fef290b5d31b5bcd1edd999ac8a6dd31c44a.js
public/assets/stimulus-loading-3576ce92b149ad5d6959438c6f291e2426c86df3b874c525b30faad51b0d96b3.js
public/assets/stimulus.min-dd364f16ec9504dfb72672295637a1c8838773b01c0b441bd41008124c407894.js
public/assets/trix-686ab55c2aea8035a7f728b61ec7afedfe857f70d6279ab453da775b7469e9e8.js
public/assets/turbo-84e828a0e6f1f9418a277df89df877e9a30524438cf8dc007c066655017a44bc.js
public/assets/turbo.min-cd3ce4205eaa3eb1f80c30fedaf47bccb15a7668eb53b1cb1a5e0dda16009d4d.js
בקובץ app/views/layout.html.erb
השורה הבאה טוענת קובץ js שנבנה דרך מנגנון זה:
<%= javascript_include_tag "application", "data-turbo-track": "reload", type: "module" %>