---
keyword: [Extend, PMD Scripting, Scripts]
nav: wcp_docs
parent_url: /wcp_docs/
title: >- 
  Concept: Script Modules
layout: subsection
---

## <a id="section_overview"></a>Script Modules

A script module is a `.script` file containing PMD scripts, which you can define a single time and invoke multiple times from different PMDs of a custom app. The last statement in a script module must return a map object, which contains properties that export its static variables and function declarations to the PMDs.

## <a id="section_creatingScriptModules"></a>Creating Script Modules

Use the Workday Extend Plugin for IntelliJ to add script modules to a custom app. Create script modules in the `scripts` subfolder under the `presentation` folder of the app.

When you create a script module, consider these guidelines:

-   The script module name must start with a letter, and must contain only letters, numbers, and underscores. Use a naming convention that aligns with the data model or script functionality. Example: If a script module contains functions working with employeeImages, use employeeImages as part of the script module name.
-   The script file has a maximum size of 100 KB. Consider chunking your script modules in an organized, logical manner.
-   The script module must return a map object containing properties that export static variables and functions of the script module. Example:

    ```
    {
        "property1": someFunction,
        "property2": someStaticVariable
    }
    ```

    -   The exported properties are unmodifiable. Any attempt to modify the exported properties and their nested properties receives a runtime exception.
    -   Ensure that the exported properties are stateless, so that they always return the same value anytime a page loads the script module. Only include properties that map to static variables and function declarations that the PMDs can invoke.

        Example: This `uuid` variable isn't static because its value can change at any given time, which makes the exported property `randomId` not stateless:

        ```
        var uuid = /* some logic to generate random UUID */
        {
            "randomId": uuid
        }
        ```

        To make `randomId` stateless, export a function instead:

        ```
        var getUuid = function() {
            /* some logic to generate random UUID */
        };
        {
            "randomId": getUuid
        }
        ```


-   Don't use the predefined app variables listed in [Reference: Predefined App Variables](/wcp_docs/urd1528494500512.html). The predefined variables are only available in PMDs but not in script modules. Predefined variables are reserved keywords that you can't use within a script module. However, PMDs can pass predefined variables as input parameters to a script of a script module. If a PMD passes a predefined variable to a script, its function declaration within the script module must use a different input parameter name.


This sample `date.script` contains date calculation scripts. Note that the last statement in this script module exports the `getDaysBetween` and `getMonth` functions as `daysBetween` and `getMonth` properties, respectively.

```
var getDaysBetween = function(date1, date2) {
    /* Swap two dates if necessary. */
    if (date:after(date2, date1)) {
        var temp = date1;
        date1 = date2;
        date2 = temp;
    }

    var CENTURY = 36500;
    var YEAR = 365;
    var MONTH = 30;
    var WEEK = 7;
    var DAY = 1;
    var step = CENTURY;
    var sameDay = false;
    var diff = 0;

    while (true) {
        var newDate = date:add(date2, 'DAY', step);
        if (date:after(date1, newDate)) {
            date2 = newDate;
            diff += step;
        }
        else {
            sameDay = date:year(date1) == date:year(newDate) &&
                      date:month(date1) == date:month(newDate) &&
                      date:extractValue('DAY', date1) == date:extractValue('DAY', newDate);
            if (sameDay) {
                diff += step;
                break;
            }

            if (step == CENTURY) {
                step = YEAR;
            }
            else if (step == YEAR) {
                step = MONTH;
            }
            else if (step == MONTH) {
                step = WEEK;
            }
            else if (step == WEEK) {
                step = DAY;
            }
            else {
                step = 0;
            }
        }
    }
    return diff;
};


var getMonth = function(date) {
    return date:extractValue('MONTH', date);
};

/* Export the getDaysBetween and getMonth functions. */
{
    "daysBetween": getDaysBetween,
    "getMonth": getMonth
}
```

## <a id="section_loadingScriptModules"></a>Loading Script Modules

Before a PMD can invoke the exported scripts of a script module, the PMD must include the script module so that it also loads when the page loads.

To load script modules on a page, specify the `include` property, which is an array of script modules. Example:

```
"include": [
   "scriptModule1.script", 
   "scriptModule2.script",
   "scriptModuleN.script"
]
```

Include only script modules that exist in the `presentation/scripts` folder of the custom app.

When a script module loads, the script module:

-   Sequentially runs each executable script in the module.
-   Loads function declarations into memory.
-   Exports the properties in the returned map.

    -   Any exported variable must be static. It must always return the same value anytime a PMD invokes it.
    -   Any exported function doesn't execute at this point. An exported function only executes when a PMD invokes it.

-   Provides its name as a binding variable, which enables the PMD to reference its exported properties.


## <a id="section_invokingScriptModules"></a>Invoking the Scripts of a Script Module

To invoke a script of a script module, specify:

```
<scriptModule>.<property>(<parameter>)
```

`<property>` is a property in the returned map that corresponds to either a function or a static variable in the script module. If the property corresponds to a function, include the function parameters, if any.

This PMD snippet includes the sample `date.script`. The `script` tag uses `date.daysBetween(startDate.value, endDate.value)`, which invokes the `getDaysBetween` function in `date.script`.

```
{
  "id": "simpleDateCalculationPage",
  "include": [
        "date.script"
  ],
  "script": "<%
      var updateBetween = function() {
          if (empty startDate.value || empty endDate.value) {
              between.value = '';
          }
          else {
              /* date is the script module name.
                 daysBetween is the exported property from date.script. */
              var days = date.daysBetween(startDate.value, endDate.value);
              between.value = days + '';
          }
      };
  %>",
  "presentation": {
    "pageType": "EDIT",
    "body": {
      "type": "fieldSet",
      "children": [
        {
          "type": "fieldSet",
          "title": "Calculate Days Between Two Dates",
          "collapsible": true,
          "initializeCollapsed": true,
          "children": [
            {
              "type": "date",
              "label": "Start Date",
              "id": "startDate",
              "onChange": "<% updateBetween(); %>"
            },
            {
              "type": "date",
              "label": "End Date",
              "id": "endDate",
              "onChange": "<% updateBetween(); %>"
            },
            {
              "type": "text",
              "label": "Days Between",
              "id": "between",
              "enabled": false
            }
          ]
        }
    ]
    }
  }
}
```

If a PMD uses pods, the pods can also invoke the exported scripts of the script module. Note, however, that pods can't include their own script modules. You can't specify the `include` property or the `script` tag in a pod.

## <a id="section_scriptModuleDependency"></a>Creating a Script Module Dependency

In general, we recommend that you keep script modules simple. However, if complex business requirements require you to create multiple complex script modules, consider creating a module dependency that supports reusability across the script modules. Script module dependency can help you organize your code and avoid duplication. Determine the scripts you can define in a nested script module that other script modules can call.

To create a script module dependency, insert this statement at the top of the calling script module:

```
script.include("nestedScriptModule.script");
```

The `script.include` statement accepts only 1 script module. To include multiple script modules, add a `script.include` statement for each script module.

When you create a script module dependency, consider these guidelines:

-   Ensure that the PMD uses only the exported scripts of the script modules that the PMD directly includes.

    The exported scripts of a nested script module are accessible only to its calling script module, not to a PMD. PMDs can't use the exported scripts of the nested script modules. If the PMD needs to invoke scripts of the nested script module, the PMD must also include the nested script module.

-   You can include a maximum of 2 dependency levels.

    Example: `a.script` includes `b.script` includes `c.script`. The PMD validation fails if `c.script` includes another script module.

-   Avoid circular script module dependency.

    Example: `a.script` includes `b.script` includes `a.script`.



This sample script module, `holiday.script`, performs a simple holiday lookup. Since `holiday.script` needs to get the holiday month, it includes `date.script` to invoke its exported `getMonth` script.

```
script.include("date.script");

var allHolidays =
    [
        "New Year's Day, Martin Luther King Jr. Day", "", "",
        "", "Memorial Day", "",
        "Independence Day", "", "Labor Day",
        "", "Veterans Day, Thanksgiving", "Christmas Day"
    ];

var getHolidays = function(adate) {
    /* date is the script module name.
       getMonth is the exported script from date.script. */
    var month = date.getMonth(adate);
    allHolidays[month - 1];
};

/* Export the getHolidays function. */
{
    "getHolidays": getHolidays
}
```

Using the same PMD snippet, let's add the `holiday.script` to the `include` property.

```
"include": [
      "date.script", 
      "holiday.script"
]
```

Note that the PMD also includes the `date.script` because the `script` tag in the previous PMD snippet invokes `date.daysBetween`.

The `onChange` event handler on the `holidayDate` widget uses `getHolidays` from `holiday.script`. Note that it uses the script module name as the binding variable prefix: `holiday.getHolidays(self.value)`:

```
{
  "type": "date",
  "label": "Pick a Date",
  "id": "holidayDate",
  "onChange": "<% holidays.value = holiday.getHolidays(self.value); %>"
},
{
  "type": "text",
  "label": "National Holidays:",
  "id": "holidays",
  "enabled": false
}
```

### Related Information

[Concept: PMD Scripting](/wcp_docs/oli1581402304020.html)

[Add Script Modules to an App in IntelliJ](/wcp_docs/zrv1604361416919.html)

[Reference: PMD Scripting Syntax](/wcp_docs/snh1581402351791.html)

[PMD Scripting](/wcp_docs/jab1582081565510.html)

[Reference: PMD Scripting Examples](/wcp_docs/mdp1582058758619.html)

