Using Salesforce B2C Commerce Script Modules

Starting in 13.6, B2C Commerce supports the Modules 1.1.1 CommonJS specification. This means that you can access JavaScript/B2C Commerce script modules within your storefront script code; and unlike traditional B2C Commerce script files, these modules don't have to be located in cartridges, although this is of course allowed. See Path Lookup Behavior of the Require Method below for more information.

Note:

This topic assumes you are familiar with the Modules 1.1.1 CommonJS specification.

What Is a B2C Commerce Script Module?

A CommonJS-compliant B2C Commerce script module is either a script file (with a .js, .json, or .ds extension) or a directory containing script files. A module can hide private data, while exposing public objects and methods via a free variable named exports.

Note:

A free variable is accessible within a function but is not a local variable defined within the function and isn't a parameter of the function. A free variable might be (but doesn't have to be) a global variable.

Before 13.6, you had to use the importScript and importPackage methods to access other scripts; these methods automatically export everything into the global context. Now you can access CommonJS-compliant modules using the require method (described below); and these modules don't automatically export everything into the global context.

Within a script file, you load a CommonJS-compliant B2C Commerce script module using the TopLevel.global.require(path:String) method. When the module is loaded, you can use any of its exported variables or methods.

Example 1: Creating the Mod1.Js Module

In the following example, the mod1 module exports the doit() method and the aaa variable, but hides the localMethod().
exports.aaa = "bbb";
exports.doit = function() {
	return "done.";
}
var localVariable = 1;

/**
* @param {String} Error message.
* @returns {Error} New object.
*/
function localMethod(message, localVariable) {
	return new Error({message: localVariable});
}

Example 2: Loading the Mod1 Module

In the following example, the script loads the mod1 module, retrieves the aaa variable from the module and runs the doit() method.

/*
 * @output ModuleId : String
 * @output Mod1_1 : String
 * @output Mod1_2 : String
 */
var m1 = require( './mod1.js' ); 

function execute( pdict )
{
	pdict.ModuleId = module.id;
	pdict.Mod1_1 = m1.aaa;
	pdict.Mod1_2 = m1.doit();
	
	return PIPELET_NEXT;
}

Path Lookup Behavior of the Require Method

B2C Commerce's TopLevel.global.require(path:String) method has different lookup behavior than the require() function as specified in the Modules 1.1.1 CommonJS specification:
  • If the path argument starts with "./" or "../" then it loads relative to the current module. The module can be a file or a directory. A file extension is acknowledged, but not required. If it's a directory, a package.json or a main file is expected in the directory.

    Note:

    A main file is a file named main, with either a .js, .ds, or .json extension.

    If the package.json file does not contain a main property, then the script defaults to the main file in the directory, if one exists. Access to parent files can't go beyond the top-level version directory. Access to other cartridges is allowed.

  • If the path argument starts with "*/" it's a path relative to the start of the cartridge path.
  • If the path argument starts with "~/" it's a path relative to the current cartridge in the cartridge path.
  • A path argument that prepends "dw", or that starts with "dw/", references B2C Commerce built-in functions and classes, for example:
     var u = require( 'dw/util' );
    loads the classes in the util package, which can be then used like this:
    var h = new u.HashMap();
  • A path argument that doesn't start with "./" or "../" is resolved as top level module.
    • The path argument is used to find a folder in the top-level version directory, typically a cartridge itself, but it can also be a simple folder.
    • If nothing is found, the path argument is used to look into a special folder (named modules) in the top version directory. You can use this folder to manage different modules. For example, you can drop a module like express.js into that folder.
If the TopLevel.global.require(path:String) method is used to reference a file, an optional file extension is used to determine the content of the file. Currently, B2C Commerce supports the following extensions, listed in priority order:
  • .js - JavaScript file
  • .ds - B2C Commerce script file
  • .json - JSON file
Note: If you use JSON to define a module, the entire JSON object is implicitly exported via the exports variable.
Note: You can still use the importScript and importPackage methods. However, it's recommended that you replace them with the TopLevel.global.require(path:String) method.

Example: Path Lookup for Mymodule.Js

Assume you have a script (used in SiteGenesis) with the following call to the require() method:
var u = require( 'mymodule');
Further assume the following files and structure on the server:
TOP_LEVEL_VERSION_DIRECTORY
│     
│   mymodule.js    (2)
│
├───modules                         // Directory
│       mymodule.js  (3)
│
├───mymodule                        // Directory
│       myScript.ds  (1)
│       package.json                // References myScript.ds
│       main.js                   
│
├───SiteGenesis Storefront Core     // Cartridge directory
└─── Controllers   // Cartridge directory
Note:

This structure includes directories and files that are outside of cartridges. To upload such directories and files to a version directory, you can use the B2C Commerce Build Suite cartridge.

Regardless of where the script that requires a module is located, B2C Commerce finds the module as follows:
  1. First, B2C Commerce searches the mymodule directory for a package.json file.
    1. In this example, the package.json is present, so B2C Commerce checks the value of the main property in the file.
    2. This property references the myScript.ds file, so B2C Commerce loads myScript.ds as a module.
    3. If there is no package.json or if the main property is missing, B2C Commerce searches the directory for a main file to load.
    4. If B2C Commerce finds a main file, the file is loaded; otherwise, B2C Commerce continues to search.
  2. Next, B2C Commerce finds the mymodule.js file because it's in the top level version directory. If there is no file at this level, B2C Commerce continues to search.
  3. Last, B2C Commerce searches the modules directory in the top level for a mymodule script file.

Although, Salesforce provides a sophisticated fallback mechanism for scripts in the global namespace, we generally discourage polluting the global namespace with utility scripts. We recommend adding scripts to specific project cartridges.