SFRA Modules

You can access JavaScript/B2C Commerce script modules that conform to the Modules 1.1.1 CommonJS specification within your storefront script code.

For information about the Modules 1.1.1 CommonJS specification, see Modules 1.1.1.

CommonJS Modules and Require

You can use CommonJS modules to share reusable functionality. Multiple controllers can reuse the same module.

A module is a .ds or .js file. You put module files either in the special modules folder or in a cartridge’s script folder.

The modules folder is a peer of cartridge folders. Commerce Cloud provides it for globally shared modules, such as third-party modules.

+-- modules
    +-- mymodule1.js
+-- My_Cartridge_1
    +-- cartridge
        +-- scripts
            +-- mymodule2.js
+-- My_Cartridge_2
    +-- cartridge
        +-- scripts
            +-- mymodule3.js

You can access modules in your cartridge, in other cartridges, or in the modules folder.

Storefront Reference Architecture (SFRA) provides a server module in the modules folder. This name is reserved for this module and editing the server module voids any promise of backward compatibility or support from Commerce Cloud.

Accessing Modules

Commerce Cloud B2C Commerce supports CommonJS require syntax to access modules and relative paths.

Use this syntax:
Syntax Use Example
~ Specifies the current cartridge name. require ('~/cartridge/scripts/cart')
. Specifies the same folder (as with CommonJS). require('./shipping');
.. Specifies the parent folder (as with CommonJS). require('../../util')
*/ Searches for the module in all cartridges that are assigned to the current site from the beginning of the cartridge path. The search is done in the order of the cartridges in the cartridge list. This notation makes it easier to load modules for a logical name. You don't have to remember what cartridge is the first in the cartridge path for a site to contain the module. For example, you can have multiple plug-in cartridges that each have a copy of the module. This syntax lets you add new plug-ins and always use the module that is first in the cartridge path. var m = require('*/cartridge/scripts/MyModule');

ImportPackage vs. Require

You can use ImportPackage statement to import B2C Commerce packages into scripts used by pipeline script nodes.

However, we recommend that you use require to import B2C Commerce script packages instead of ImportPackage. For example: require(β€˜dw/system/Transaction’)

Important:

Using the require() function to load a module has an impact on performance. To handle a request, a controller is loaded and then executed on a global level, just as exported methods are executed. When the controller module is initialized, the actual controller function is invoked.

If the dependencies to other modules are initialized on a global level, many modules can be loaded unnecessarily. If possible, place require() statements in the most limited scope that is appropriate (for example, as local variables within a function body). If your require() statements are placed at a global level, they are loaded for every request. For example, require() statements at the top of a controller file are loaded with each request. Globally required modules are loaded even if they are not needed in the current execution context.

If all modules execute their require() statements globally, all modules are loaded for every request. This overhead can significantly degrade performance, depending on the number of modules. We suggest, instead, that you move the require() calls for non-API modules into the function bodies so that they are resolved only when necessary. A require() isn't an import; it's a real function call. An import in Java, in contrast, is only a compiler directive and has no effect at run time.

Understanding Cartridge Path Inheritance for Controllers and Pipelines

When a request arrives for a specific URL:
  1. B2C Commerce searches the cartridge path for a matching controller. If the controller is found, it's used to handle the request.
  2. If no controller is found, the cartridge path is searched again for a matching pipeline. If the pipeline is found, it's used to handle the request.
Note: If a cartridge contains a controller and a pipeline with a matching name, the controller takes precedence.

When searching the cartridge path, B2C Commerce doesn't verify whether the controller contains the called function in the requesting URL. Calling a controller function that doesn't exist causes an error.

The platform simply searches the controllers folder. Therefore, it doesn't matter what type of cartridge contains the controllers. They can be in a SiteGenesis JavaScript Controllers (SGJC) cartridge or a Storefront Reference Architecture (SFRA) cartridge.

Inheriting and Overriding Modules in Your Cartridge Stack Using Module.SuperModule

Often, you can have several layers of cartridges that overlay one another. Each cartridge can import from the previous cartridge and overlay it. To make this easy, Commerce Cloud provides a chaining mechanism that lets you access modules that you intend to override.

The module.superModule global property provides access to the most recent module on the cartridge path module with the same path and name as the current module.

B2C Commerce searches cartridges after the current cartridge on the site cartridge path. It searches for a module with the same name and in the same location in the cartridge as the current module. B2C Commerce skips cartridges before the current cartridge in the path, and it skips the current cartridge, and only searches cartridges after the current cartridge. It continues searching through all the remaining cartridges in the cartridge path until a matching module is found. If no matching module is found, module.superModule returns null.

Note:

To create a module that inherits from an existing module, create the existing module in your cartridge and require it in your new module. For example, suppose that you want to create a home page for a specific customer group, named BigSpenderHome.js. To create the homepage, first create Home.js in your cartridge. Next, modify it to include (require) the functionality of Home.js from your cartridge stack.

Example: Overriding and Extending an Existing Controller:

This example shows how the Page.js module is inherited from the last cartridge in the stack and overrides the Show function. Specifically, it changes the data model used to render the page to show _Hello Commerce Cloud._

//Page.js
var page = module.superModule; // require functionality from last controller in the chain with this name
var server = require('server');
 
server.extend(page);
 
server.append('Show', function(req, res, next) {
    res.setViewData({ value: 'Hello Commerce Cloud' });
    next();
});
 
module.exports = server.exports();

SuperModule Scenarios

For example, suppose that you are modifying the Page.js file, and it defines a page variable using the module.superModule property:

//Page.js
var page = module.superModule;

The Page.js file is located in the folder mysite/cartridges/cartridge_B/cartridge/controllers.

Suppose that you have the following cartridge path:

cartridge_A: cartridge_B: cartridge_C: cartridge_D: app_storefront_base
B2C Commerce searches the path starting with cartridge_C.
If: The platform returns:
All cartridges contain a Page.js file in the same location as cartridge_B. cartridge_C module Page.js module- because it's the next cartridge after cartridge_B in the path
cartridge_A and cartridge_D contain a Page.js file in the same location as cartridge_B. cartridge_D module Page.js module - because it's after cartridge_B in the path
Only cartridge_A contains a Page.js file in the same location as cartridge_B. null - because cartridge_A isn't after cartridge_B in the path
All cartridges contain a Page.js file in a different location than cartridge_B. null - because none of the files match the location of the file in the current cartridge.
cartridge_C contains a Page.js file in a different location and app_storefront_base contains a Page.js file in the same location as cartridge_B. app-storefront_base Page.js module - because the files must match name and location.

The power of the module.superModule mechanism is that it lets you chain overlay cartridges on top of one another. For example, if cartridge_C and cartridge_D both use the module.superModule function and both add functionality, all of that functionality is available to cartridge_B.

Important: Chaining depends on an expected order of cartridges in the site cartridge path. Therefore, changing the order of cartridges in the cartridge path can impact your code. To minimize code changes, think about the order of your cartridge layers in advance.

Global Modules

You can make a script globally available by adding it to the modules folder. We recommend that you add the script to the storefront-reference-architecture repository’s modules folder. We recommend a single modules directory in one repository per site. This approach makes upload work in a predictable manner. The uploadCartridge tool deletes the existing modules folder before upload, and the upload tool doesn't identify or prevent naming collisions when uploading files.