SiteGenesis Modules and Hooks

There are a number of features specific to SiteGenesis that you might not be familiar with if your storefront is based on SiteGenesis Pipelines (SGPP). Most of the features described here focus on reusability of code and can be useful when migrating your storefront code.

SiteGenesis uses the following features:

CommonJS Modules and Require

CommonJS modules let you create scripts with functionality that can be reused by multiple controllers. A module is a .ds or .js file and is usually stored in a cartridge in the script folder or in a modules folder at the same level as a cartridge. You can access modules in your cartridge, other cartridges, and the modules folder.

The modules folder is a peer of cartridge folders. Salesforce recommends using 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

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.

See also Using Salesforce B2C Commerce Script Modules

Accessing Modules

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

Use the following 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 if you don't know what cartridge is the first in the cartridge path for a site to contain the module. For example, you can have multiple plugin cartridges that each have a copy of the module. This lets you add new plugins and always use the module that is first in the cartridge path. var m = require('*/cartridge/scripts/MyModule');

ImportPackage vs. Require

In previous versions of SiteGenesis, the ImportPackage statement was used to import B2C Commerce packages into scripts used by pipeline script nodes.

You can also use require to import B2C Commerce script packages. For example: require(β€˜dw/system/Transaction’)

Salesforce recommends using the require method to import B2C Commerce script packages, or other JavaScript or B2C Commerce script modules instead of ImportPackage.

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 all initialized on the global level (like the require() calls are at the beginning of the controller file) and not on a function level (as local variables within a function body) there can be more modules loaded than are actually needed by the used function. For example, if there are several cases in the business logic of the controller and only one case is followed for a specific request, it doesn't make sense to load the modules for the other cases, as they are never used.

If all modules execute their requires globally, this ends up in practically all modules being loaded on every request. This can significantly degrade performance, depending on the number of modules. It's therefore suggested to move the require() calls for non-API modules into the function bodies so that they are resolved lazily and only when they are really needed. A require() isn't an import, it's a real function call,. An import in Java, in contrast, is only a compiler directive and doesn't have any effect at run time.

Hooks

Hooks configure a piece of functionality to be called at a specific point in your application flow or at a specific event.

There are three types of hooks you can use with SiteGenesis:

  • B2C Commerce hooks – B2C Commerce provides onSession and onRequest hooks to replace onSession and onRequest pipelines. It also provides hooks for Apple Pay on the Web.
  • OCAPI hooks – B2C Commerce provides extension points that let you automatically call scripts before or after specific OCAPI calls. These hooks are defined by Commerce Cloud. They are called
  • Custom hooks – you can define custom extension points and call them in your storefront code using the B2C Commerce script System package HookMgr class methods. You can then access these hooks in either OCAPI or your storefront code. This makes them useful for functionality in a multichannel set of applications based on the same site.

Hook Definition

The package.json file points to the hook file for a cartridge, using the hooks keyword.

Example: package.json

The package.json file defines where the hooks file is located and what it is named.

{
   "hooks": "./cartridge/scripts/hooks.json"
}

The hook file defines a uniquely named extension point and a script to run. Hook scripts must be implemented as CommonJS modules, so the script identifier is a module identifier and can be a relative path or any other valid module identifier.

Example: hook.json

This file defines a dw.ocapi.shop.basket.calculate hook that calls the calculate.js script.

{
	"hooks": [
       	{
			"name": "dw.ocapi.shop.basket.calculate",
			"script": "./cart/calculate.js"
       	},
        {
			"name": "dw.system.request.onSession",
            "script": "./request/OnSession"
       	},
        {
			"name": "app.payment.processor.default",
			"script": "./payment/processor/default"
       	}
	]
}

This example shows an OCAPI hook, a controller hook, and a custom hook. The OCAPI hook runs a script to calculate the cart. The controller hook calls the OnSession.js script in the scripts/request directory. The custom hook can be called if you use the System package HookMgr class callHook method

Example: calling a custom hook

This is the code that calls the hook from calculate.js.

return dw.system.HookMgr.callHook('app.payment.processor.default', 'Handle', {
            Basket : cart
        });

Running Multiple Hooks for an Extension Point

It's possible to register multiple modules to call for an extension point in a single hooks.json file. However, you can't control the order in which the modules are called. Also, if you call multiple modules, only the last hook called returns a value. All of the modules are called, regardless of whether any of them return a value.

At run time, B2C Commerce runs all hooks registered for an extension point in all cartridges in your cartridge path, in the order of the cartridges on the path. So if each cartridge registers a module for the same hook, the modules are called in cartridge path order of the cartridges they are registered in.

Note: Because hooks are called in the order of the cartridges on the path, if you change the order of the cartridges, you also change the order in which hooks are called.

Error Logging

Controller and script logging is available in the:

  • Custom error log – this log contains the hierarchy of controller and script functions and line numbers related to exceptions thrown. This is intended for use by developers to debug their code.
  • System error log – this is primarily useful for Commerce Cloud Support.

Example: Custom error log

Error while executing script 'test_cartridge_treatascustom/cartridge/controllers/TestController.js': Wrapped com.demandware.beehive.core.internal.template.ServletAbortException: Requested template 'controller/testController' not found! (test_cartridge_treatascustom/cartridge/controllers/TestController.js#21)
    at test_cartridge_treatascustom/cartridge/controllers/TestController.js:21 (isml)
    at test_cartridge_treatascustom/cartridge/controllers/TestController.js:52 (anonymous)

Back to top.