The plugin checks templates as you type and offers a fix for most problems. Every ISML inspection is in Settings › Editor › Inspections › SFCC › ISML, where you can change its level or turn it off. Turning off the whole ISML group is the JetBrains counterpart of the VS Code intellij-sfcc.isml.linter setting
Checks that read only the template and the tag catalog work without a license and while the IDE indexes. Checks that read other files, such as templates, controllers and resource bundles, need a license and wait until indexing ends, so they never report a problem from a half-built index
Syntax
All syntax checks are free:
| Inspection | Level | Message | Fix |
|---|---|---|---|
| ISML tag outside its parent tag | Error | <isnext> outside <isloop>, A second <iselse> in one <isif> | Remove <isnext>, Remove <iselse> |
| Branch after iselse | Error | <iselseif> after <iselse> | Move before <iselse>, which moves the branch with its body |
| ISML closing tag that closes nothing | Warning | </isset>: <isset> has no closing tag | Remove </isset> |
| Self-closed paired ISML tag | Warning | <isif> must not be self-closed | Remove '/' when an end tag follows, otherwise Expand to a start and an end tag |
| Redundant iselse or iselseif closing tag | Weak warning | Redundant </iselse> | Remove </iselse> |
| ISML tag name not in lower case | Weak warning | ISML tag names are lower case | Convert the tag name to lower case |
Attributes
The attribute checks read the tag catalog, so they're free too. Only the custom tag parts need a license, see Custom Tags
| Inspection | Message | Fixes |
|---|---|---|
| Unknown ISML attribute | Unknown attribute 'tempate' of <isinclude> | Change to 'template' for a close name, Remove 'tempate' |
| Missing required ISML attribute | <isset> needs 'value', <isinclude> needs 'template' or 'url' | Add 'value', or one Add fix for each choice |
| Conflicting ISML attributes | 'template' cannot be combined with 'url' | Remove one of them |
| Duplicate ISML attribute | A second copy of an attribute | Remove the copy |
| Invalid ISML attribute value | 'scope' takes literal text, not an expression, 'items' takes an expression: 'pdict.products' is literal text, 'sesion' is not a value of 'scope': page, request, session, 'hour' takes a whole number | Remove ${}, Wrap in ${}, Change to 'session' for a close value |
| Deprecated ISML tag, attribute or value | 'iterator' is deprecated, use 'items', shown struck through | Change to 'items' when the catalog names a replacement |
| iscontent not first or repeated | More than one <iscontent>, <iscontent> after <div>, which can write output | Remove the duplicate <iscontent>, Move to the top of the file |
| iscontent type does not match the data | A JSON or XML body under a text/html <iscontent>, as a weak warning | Change to the type that fits |
| ISML attribute set to its default | Off by default. A weak warning on scope="session" and other defaults | Remove the attribute |
Best practice
| Inspection | Message | Fixes |
|---|---|---|
| isset without scope | <isset> without scope keeps total for the whole session | Add scope="page", Add scope="request", Add scope="session" |
| iscache settings | <iscache> without type, Another <iscache> in this template has other settings, <isredirect> after <iscache>: the redirect is cached too | Add type="relative", Remove this <iscache> |
| Template includes itself | This template includes itself through product/tile → product/productTile. Needs a license | Open the template that closes the loop |
Templates, routes and resources
These checks resolve names against other files, so they need a license:
| Inspection | Level | Message | Fixes |
|---|---|---|---|
| Unresolved ISML template | Error | Cannot resolve template 'product/loyaltyBanner' | Create template, which asks for the cartridge: this one first, then the cartridge path |
| Error | Cannot resolve template 'product/Tile': the file is 'product/tile', and template paths are case-sensitive | Change to 'product/tile', in the path style you wrote | |
| Error | Template 'account/rewards' is not on the cartridge path of site RefArch; it exists in plugin_loyalty | None. Add the cartridge to the site's cartridge path | |
| Weak warning | No template matches 'product/components/*', for a path built from an expression, where * stands for the expression | None | |
| Unknown ISML custom tag | Warning | Unknown custom tag <isloyaltybage>: no <ismodule> declares it | See Custom Tags |
| Unresolved route in an ISML template | Warning | Cannot resolve route 'Loyalty-Shwo'. Only literal Controller-Action names are checked | Change to 'Loyalty-Show' for a close route |
| Unresolved Resource bundle or key in an ISML template | Warning | Cannot resolve bundle 'acount', Cannot resolve key 'heading.loyalty' in bundle 'account' | Create key 'heading.loyalty' in bundle 'account', Use bundle 'common', which has the key |
A created template starts from the ISML Template text in Settings › Editor › File and Code Templates and goes under templates/default/ of the cartridge you pick
Output encoding
B2C encodes ${} and <isprint> for HTML unless told otherwise. That's right in markup and wrong in a <script> block or CSS. These checks find where each value lands and report an encoding that doesn't fit:
| Inspection | Message | Fixes |
|---|---|---|
| isprint with encoding off | encoding="off" prints the value as it is into a string in a <script> block; expected "jsblock" | Change to encoding="jsblock" |
| isprint encoding does not fit the output position | encoding="htmlcontent" does not fit a string in a <script> block; expected "jsblock", No encoding protects a value in <script> code outside a string literal; print it inside a string literal | Change to encoding="jsblock" |
| HTML-encoded output in script or CSS | ${} uses HTML encoding, which does not protect a string in a <script> block; expected "jsblock" | Wrap in <isprint encoding="jsblock"> |
| Server-side ${} in a JavaScript template literal | B2C evaluates ${name...} on the server, where 'name' is not defined; the template literal never sees it. Needs a license | Convert the template literal to string concatenation |
The encoding checks share two options. Expressions treated as safe lists expressions that are never reported, where * matches any text. Ignore string literals (on) skips ${'text'}. isprint with encoding off skips HTML and XML text, where encoding="off" usually prints trusted markup, unless you turn on Report HTML and XML text. HTML-encoded output in script or CSS reports values in CSS only with Report values in CSS, since no encoding protects them there
Template variables
These checks read scripts inside the template, so they wait until indexing ends:
| Inspection | Message | Fixes |
|---|---|---|
| Unknown pdict member | No render call of this template sets 'pdict.nextReward'. Needs a license | Change to a close member name, Declare 'pdict.nextReward' with @pdict, Add 'nextReward' to the render model in Loyalty.js |
| Template variable used before its declaration | 'total' is used before the <isset> that declares it | Move <isset> above the first use |
| Template variable used after isremove | 'total' is used after <isremove> removed it | Remove <isremove> |
| Unused template variable | Template variable 'total' is never read, also for loop variables and loop status | None |
| Attribute expression type mismatch | ... is always true as a condition: test its size, or use empty(), <isloop> cannot iterate over ..., Expected a number, found ..., as weak warnings | None |
| Script syntax the compatibility mode does not support | Compatibility mode 21.2 does not support template literals (22.7 or later), No B2C compatibility mode supports optional chaining. Error, free | None |
Add to the render model adds the member to the object literal of the render call, keeping its layout and trailing comma, and leaves the caret after nextReward: in the controller. It's offered for up to three render calls that pass an object literal. Members are checked only in templates whose render calls are all known, see Scripting in ISML
Suppressing a problem
Alt+Enter on a problem, then the arrow next to a fix, offers Suppress for tag and Suppress for file. Both write a comment that B2C never sends to the browser:
<iscomment>noinspection IsmlIssetWithoutScope</iscomment>
<isset name="basket" value="${pdict.basket}" />Before a tag, the comment covers that tag and everything inside it. As the first item of the file, it covers the whole file. It works for problems inside ${} and <isscript> too
HTML warnings the plugin hides
ISML templates are HTML with tags that run on the server, so plain HTML checks raise false alarms. The plugin filters them:
- Closing tag matches nothing and Start tag has wrong closing tag are hidden when the tags pair as written across
<isif>branches, such as a<div>opened in both branches and closed once after</isif>. A stray end tag is still reported - HTML parse errors next to ISML tags and
${}are hidden - HTML and XML inspections that would read an ISML value are suppressed, such as Unknown target on
href="${URLUtils.url('Cart-Show')}"