Magento 2 – Using Alpinejs With RequireJS

After hearing the great talk of Willem in Reacticon, I had a plan to explore Alpinejs in a Magento site. I spent some time exploring his idea in a Magento 2 site without removing the requirejs trouble we have right now with the Luma and black theme in Magento 2.

However, I stumbled upon the below error when I try to use Alpinejs along with requirejs. The error is shown below:

require.js:166 Uncaught Error: Mismatched anonymous define() module: function(){"use strict";function e(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function t(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,i)}return n}function n(n){for(var i=1;iAlpine: [${t}] directive should only be added to <template> tags. See https://github.com/alpinejs/alpine#${t}):1!==e.content.childElementCount&&console.warn(Alpine: <template> tag with [${t}] encountered with multiple element roots. Make sure <template> only has a single child element.)}function o(e){return e.toLowerCase().replace(/-(\w)/g,(e,t)=>t.toUpperCase())}function a(e,t){var n;return function(){var i=this,r=arguments;clearTimeout(n),n=setTimeout(function(){n=null,e.apply(i,r)},t)}}function l(e,t,n={}){return"function"==typeof e?e.call(t):new Function(["$data",…Object.keys(n)],var __alpine_result; with($data) { __alpine_result = ${e} }; return __alpine_result)(t,…Object.values(n))}const c=/^x-(on|bind|data|text|html|model|if|for|show|cloak|transition|ref|spread)\b/;function u(e){const t=m(e.name);return c.test(t)}function d(e,t,n){let i=Array.from(e.attributes).filter(u).map(f),r=i.filter(e=>"spread"===e.type)[0];if(r){let e=l(r.expression,t.$data);i=i.concat(Object.entries(e).map(([e,t])=>f({name:e,value:t})))}return n?i.filter(e=>e.type===n):function(e){let t=["bind","model","show","catch-all"];return e.sort((e,n)=>{let i=-1===t.indexOf(e.type)?"catch-all":e.type,r=-1===t.indexOf(n.type)?"catch-all":n.type;return t.indexOf(i)-t.indexOf(r)})}(i)}function f({name:e,value:t}){const n=m(e),i=n.match(c),r=n.match(/:([a-zA-Z0-9-:]+)/),s=n.match(/.[^.]]+(?=[^]]*$)/g)||[];return{type:i?i[1]:null,value:r?r[1]:null,modifiers:s.map(e=>e.replace(".","")),expression:t}}function m(e){return e.startsWith("@")?e.replace("@","x-on:"):e.startsWith(":")?e.replace(":","x-bind:"):e}function p(e,t=Boolean){return e.split(" ").filter(t)}const h="in",v="out";function b(e,t,n,i=!1){if(i)return t();if(e._x_transition&&e.__x_transition.type===h)return;const r=d(e,n,"transition"),s=d(e,n,"show")[0];if(s&&s.modifiers.includes("transition")){let n=s.modifiers;if(n.includes("out")&&!n.includes("in"))return t();const i=n.includes("in")&&n.includes("out");(function(e,t,n){const i={duration:g(t,"duration",150),origin:g(t,"origin","center"),first:{opacity:0,scale:g(t,"scale",95)},second:{opacity:1,scale:100}};x(e,t,n,()=>{},i,h)})(e,n=i?n.filter((e,t)=>t["enter","enter-start","enter-end"].includes(e.value))?function(e,t,n,i){let r=n=>"function"==typeof n?t.evaluateReturnExpression(e,n):n;const s=p(r((n.find(e=>"enter"===e.value)||{expression:""}).expression)),o=p(r((n.find(e=>"enter-start"===e.value)||{expression:""}).expression)),a=p(r((n.find(e=>"enter-end"===e.value)||{expression:""}).expression));(e,s,o,a,i,()=>{},h)}(e,n,r,t):t()}function y(e,t,n,i=!1){if(i)return t();if(e._x_transition&&e.__x_transition.type===v)return;const r=d(e,n,"transition"),s=d(e,n,"show")[0];if(s&&s.modifiers.includes("transition")){let n=s.modifiers;if(n.includes("in")&&!n.includes("out"))return t();const i=n.includes("in")&&n.includes("out");(function(e,t,n,i){const r={duration:n?g(t,"duration",150):g(t,"duration",150)/2,origin:g(t,"origin","center"),first:{opacity:1,scale:100},second:{opacity:0,scale:g(t,"scale",95)}};x(e,t,()=>{},i,r,v)})(e,n=i?n.filter((e,t)=>t>n.indexOf("out")):n,i,t)}else r.some(e=>["leave","leave-start","leave-end"].includes(e.value))?function(e,t,n,i){const r=p((n.find(e=>"leave"===e.value)||{expression:""}).expression),s=p((n.find(e=>"leave-start"===e.value)||{expression:""}).expression),o=p((n.find(e=>"leave-end"===e.value)||{expression:""}).expression);(e,r,s,o,()=>{},i,v)}(e,0,r,t):t()}function g(e,t,n){if(-1===e.indexOf(t))return n;const i=e[e.indexOf(t)+1];if(!i)return n;if("scale"===t&&!E(i))return n;if("duration"===t){let e=i.match(/([0-9]+)ms/);if(e)return e[1]}return"origin"===t&&["top","right","left","center","bottom"].includes(e[e.indexOf(t)+2])?[i,e[e.indexOf(t)+2]].join(" "):i}function x(e,t,n,i,r,s){e.__x_transition&&(cancelAnimationFrame(e.__x_transition.nextFrame),e.__x_transition.callback&&e.__x_transition.callback());const o=e.style.opacity,a=e.style.transform,l=e.style.transformOrigin,c=!t.includes("opacrequire.js:166)
at intakeDefines (require.js:1221)
at require.js:1408

This error indicates Alpinejs just simply wont work with Requirejs library. The define function of requirejs is now conflicting with the define function in Alpinejs. Please note, I tried to include Alpinejs in the bottom section of the page.

So the immediate solution came to my mind on this problem is, including the Alpinejs library before Magento setting up requirejs. If we check the page source of any Magento 2 site. We will see below requirejs initialization.

<!doctype html>
<html lang="en">
    <head >
        <script>
    var BASE_URL = 'http://magento2site.test/';
    var require = {
        "baseUrl": "http://magento2site.test/static/version1603447215/frontend/Mytheme/custom/en_US"
    };
</script>
....
</head>
....
</html>

We need to include Alpinejs library just above this script. In order to do that, you need to edit the phtml file corresponding to the above script in your theme. So we can do this:

File: `app/design/frontend/Mytheme/custom/page/js/require_js.phtml`

<script src="<?= $block->getViewFileUrl('Magento_Theme::js/lib/alpine.min.js') ?>"></script>
<script>
    var BASE_URL = '<?= $block->escapeUrl($block->getBaseUrl()) ?>';
    var require = {
        "baseUrl": "<?= $block->escapeUrl($block->getViewFileUrl('/')) ?>"
    };
</script>

Now after flushing the cache and reload the page again, you will see the Alpinejs is included at top of the page in the page source and errors are gone forever. Now Alpinejs is ready to do it’s job.

<html lang="en">
    <head >
        <script src="http://magento2site.test/static/version1603447215/frontend/Mytheme/Custom/en_US/Magento_Theme/js/lib/alpine.min.js"></script>
<script>
    var BASE_URL = 'http://magento2site.test/';
    var require = {
        "baseUrl": "http://magento2site.test/static/version1603447215/frontend/Mytheme/custom/en_US"
    };
</script>
....
</head>
....
</body>

Please note, we are loading the script synchronously here. Adding async attribute in the Alpinejs script tag will lead you to the conflict of RequireJS again. Unfortunately this is an inevitable issue. Fortunately, the size of Alpinejs is too small so that, it won’t be a huge performance killer.

It makes so much sense to create custom js components in using Alpinejs which make the coding much more simpler and fun. Get away from UiComponents and use such a cool library like AlpineJS makes much more sense to me.

If time allows, we can even try to replace existing requirejs functionalies with Alpinejs which will provide a huge performance improvement.

Kudos to my friend Willem for giving such an interesting idea. I will keep on exploring this in our next project

Leave a Reply

Your email address will not be published. Required fields are marked *