8

In Magento 2, you can use RequireJS to include a javascript module with code that looks something like this.

#File: app/code/Package/Name/view/frontend/requirejs-config.js
var config = {
    map: {
        '*': {
            modulename: 'Package_Name/js/path/to/file'
        }
    }
}

While the requirejs-config.js file is a bit of Magento 2 magic, this appears to be standard RequireJS. You're basically mapping the short name modulename to the javascript module named Package_Name/js/path/to/file.

What's not clear is where or how Magento 2 converts the javascript module name above

Package_Name/js/path/to/file

Into the HTTP(S) url

//magento.example.com/static/frontend/Magento/luma/en_US/Package_Name/js/path/to/file.js

In a stock RequireJS system, RequireJS would try to load the following URL

//magento.example.com/Package_Name/js/path/to/file.js

So clearly Magento's doing something to ensure the above URL is converted into a Magento frontend URL. What's not clear is

  1. Where this happens (PHP layer? Javascript layer?)
  2. What the rule for that conversion are. The RequireJS module doesn't look like a standard Magento file identifier (Package_Name::js/path/to/file)

So, how/where does Magento 2/RequireJS convert the module into a path.

7

RequireJS has a feature that allows you to set a custom baseUrl.

Magento generates baseUrl for RequireJS in the body of the page, where JS resources are requested. So, it's basically one more part of RequireJS config generated by Magento. This baseUrl is calculated on server side and is based on current theme, locale and base URL for static view files for the store. Then native RequireJS functionality calculates full path as follows

baseUrl + file + '.js'
  • This doesn't seem to answer the question I asked. – Alan Storm Jan 23 '16 at 17:56
  • Could you, please, clarify the question? Magento just uses RequireJS feature of baseUrl - requirejs.org/docs/api.html#config-baseUrl and to make it working properly generates baseUrl to be equal to //magento.example.com/static/frontend/Magento/luma/en_US/ for the page. RequireJS just concatenates it with the module's path: //magento.example.com/static/frontend/Magento/luma/en_US/ + Package_Name/js/path/to/file -> //magento.example.com/static/frontend/Magento/luma/en_US/Package_Name/js/path/to/file and adds .js. – BuskaMuza Jan 24 '16 at 2:15
  • Here baseUrl is generated - github.com/magento/magento2/blob/develop/app/code/Magento/Theme/… – BuskaMuza Jan 24 '16 at 2:18
  • Useful information @BushaMuza, but again, not the question I asked. – Alan Storm Jan 24 '16 at 23:20
  • 1
    I think it is vanilla RequireJS. If the 'data-main' tag is set (which it is in default M2), it'll add the baseUrl that is being set in the file BuskaMuza references in front of the mapped JS url. The baseUrl is set to the static folder (with locale), which is for example //magento.example.com/static/frontend/Magento/luma/en_US/. Add Package_Name/js/path/to/file.js to it and you'll have the complete URL. I think this is what you are looking for; github.com/magento/magento2/blob/develop/lib/web/requirejs/… – Peter Jaap Blaakmeer Jan 25 '16 at 20:22
7

You are wondering how

Module_Name/js/path/to/module

becomes

//magento.example.com/static/frontend/Package/Theme/locale/Module_Name/js/path/to/module.js

First of all, it's important to understand that this is entirely requireJS, not any Magento special sauce (, although there is some of that elsewhere). For the most part, the front-end uses only the normal behaviour of RequireJS. The magic is usually in how it generates pub/static, namely how view/area/web/js/path/to/module.js is symlinked to pub/static/area/Package/theme/Module_Name/js/path/to/module.js. This is handled by Magento's static asset compilation process, and I don't go into it here.

requirejs-config.js

Let's introduce a new file, which you mention: requirejs-config.js. This is some Magento 2 special sauce, but probably not as much as you might think.

This file can be any JavaScript, but should at the very least declare a (global) variable called config. The object bound to config is passed directly to requireJS to configure it.

The way this works is Magento finds all of the requirejs-config.js in a project. These can be in a module, under view/area, or in a theme, in its root directory, and in a theme's module override, e.g. Magento_Catalog/requirejs-config.js. Note that this does not include any child of a web directory. This file, should, in general be a sibling to a web directory.

Once globbed, each file is decorated in a closure (so our global variable actually isn't), and a line at the end of the closure passes the config variable to the require object. This can be seen:

This is Magento_Checkout::view/frontend/requirejs-config.js:

/**
 * Copyright © 2015 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */

 var config = { 
 map: { 
 '*': { 
 discountCode: 'Magento_Checkout/js/discount-codes',
 shoppingCart: 'Magento_Checkout/js/shopping-cart', 
 regionUpdater: 'Magento_Checkout/js/region-updater', 
 opcOrderReview: 'Magento_Checkout/js/opc-order-review',
 sidebar: 'Magento_Checkout/js/sidebar', 
 payment: 'Magento_Checkout/js/payment', 
 paymentAuthentication: 'Magento_Checkout/js/payment-authentication' 
 }
 } 
};

When it gets to the front-end, it will look like this:

(function() {
 /**
 * Copyright © 2015 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */

 var config = { 
 map: { 
 '*': { 
 discountCode: 'Magento_Checkout/js/discount-codes',
 shoppingCart: 'Magento_Checkout/js/shopping-cart', 
 regionUpdater: 'Magento_Checkout/js/region-updater', 
 opcOrderReview: 'Magento_Checkout/js/opc-order-review',
 sidebar: 'Magento_Checkout/js/sidebar', 
 payment: 'Magento_Checkout/js/payment', 
 paymentAuthentication: 'Magento_Checkout/js/payment-authentication' 
 }
 } 
}; require.config(config);
})();

This decoration can be seen in Magento\Framework\RequireJs\Config.

Each of these decorated files is concatenated and dumped in static/_requirejs/area/Package/theme/locale/secure/requirejs-config.js. This location is agreed upon ahead of time, so that the HTML loads this script as it loads requireJS:

<script type="text/javascript" src="https://magento.example.com/static/area/Package/theme/locale/requirejs/require.js"></script> <script type="text/javascript" src="https://magento.example.com/static/_requirejs/area/Package/theme/locale/secure/requirejs-config.js"></script>

I consider how to configure RequireJS out of scope for this answer, but they have fairly good documentation on this. There are two important things to note, though:

  1. Successive calls to require.config will layer objects on top of each other, so last write wins. They don't replace, which is crucial.
  2. There is one configuration at the top of this configuration that sets baseUrl. This is not in a requirejs-config.js. This is inserted at compile time by Magento\Framework\RequireJs\Config.

Forgetting for a moment how Magento works out which RequireJS modules need loading (a good discussion for another time, perhaps; As a hint, look in mage/apply/main.js), let's suppose we have the code:

require(['modulename'], function () {});

in a vacuum somewhere. How does Magento know what to do?

Well, the first thing requireJS will do is look up modulename in its mapping. In our case, it will learn that it should treat all requests to modulename as a request to Module_Name/js/path/to/module. It only does this once. Mappings are not recursive. I repeat. If you have a mapping from a to b, and from b to a, this will swap each request, and will not cause an infinite loop.

Once we have gone through the mapping brouhaha, RequireJS looks at what it has. If it ends in .js, and doesn't look like an absolute link or a URL, it will prepend the configured baseUrl and load that script through its normal procedures. If it doesn't end in .js and isn't an absolute link or URL, it will add .js to the end, then prepend the configured baseUrl and load through its normal procedures. If requireJS reckons it has a URL, it just tries to load that.

  • OK, i've got this one. The missing piece for me was RequireJS's baseUrl configuration. i.e. "RequireJS has a feature that allows you to set a custom baseUrl, Magento uses this feature, and generates the baseURL on that backend" requirejs.org/docs/api.html#config-baseUrl – Alan Storm Jan 26 '16 at 17:22
0

It's using components further more detail you check Magento\Framework\View\Element\Js\Components class and defaul module file vendor\magento\module-catalog\view\frontend\layout\default.xml

<referenceContainer name="after.body.start">
        <block class="Magento\Framework\View\Element\Js\Components" name="head.components" as="components" template="Magento_Catalog::js/components.phtml"/>
    </referenceContainer>
  • This doesn't seem to answer the question I asked. – Alan Storm Jan 23 '16 at 17:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.