eslint-plugin-sfmc
v5.2.3
Published
ESLint plugin for Salesforce Marketing Cloud Engagement+Next - AMPscript, Server-Side JavaScript (SSJS) and Handlebars
Maintainers
Readme
eslint-plugin-sfmc
Unified ESLint plugin for Salesforce Marketing Cloud — linting rules for both AMPscript and Server-Side JavaScript (SSJS).
Installation
npm install eslint-plugin-sfmc --save-devRequires ESLint 10.0.0+ (flat config) and Node.js 22+. The ESLint minimum is a breaking requirement change; all 12 public configuration shapes remain unchanged (six objects and six multi-config arrays). Optional composition with eslint-plugin-unicorn@76 requires ESLint 10.4.0+, matching Unicorn 76's upstream peer range; the base SFMC plugin and its non-Unicorn configs remain supported from ESLint 10.0.0.
Quick Start
// eslint.config.js
import sfmc from 'eslint-plugin-sfmc';
export default [
// Lint standalone .ampscript/.amp and .ssjs files
...sfmc.configs.recommended,
// Lint AMPscript + SSJS embedded in .html files
...sfmc.configs.embedded,
];VS Code Setup
To see eslint(sfmc/...) diagnostics in VS Code for .amp, .ssjs, and .html files you need the VS Code ESLint extension to validate the custom SFMC language IDs.
Option A — Install vscode-sfmc-language (recommended)
The SFMC Language Service extension contributes the SFMC language IDs and automatically configures eslint.validate for you. No manual settings required.
Option B — Manual configuration
Add the following to your .vscode/settings.json:
{
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"html",
"vue",
"markdown",
"ampscript",
"ssjs",
"sfmc",
"handlebars"
]
}Why
eslint.validateand noteslint.probe?eslint.probesilently skips files for language IDs that the ESLint extension does not natively recognise.eslint.validateforces the extension to process those files regardless of language ID.
Diagnostic documentation links
SFMC rule metadata links to the owning plugin version's documentation under blob/v<package-version>/docs/rules/. These links also work with custom plugin namespaces. Local development uses the package version deterministically; links for a prospective release may not resolve until that version and its documentation are tagged and published. ESLint parser failures (ruleId: null) have no rule metadata link. Core and third-party rules retain their own documentation metadata.
5.2.0 release notes
- Added
sfmc/amp-prefer-boolean-literalatwarnseverity in the recommended configs. It recognizes all eight accepted boolean-like forms and recommends baretrueorfalse. - Added safe autofixes and editor quick fixes for numeric and quoted boolean alternatives while leaving invalid values to
sfmc/amp-arg-types. - Updated
ampscript-datato^4.1.0andssjs-datato^2.1.1, including the complete boolean-like parameter metadata used by both AMPscript rules. - No existing rule IDs, public configuration shapes, or supported inputs were removed or changed incompatibly.
Version 5.2.3 exports 53 SFMC rules. Every rule retains version-pinned documentation metadata, and release checks verify the tagged documentation and packed npm artifact. The AMPscript parser adapter exposes ESLint-compatible comment tokens, allowing core comment rules such as spaced-comment to validate and autofix comments without changing the canonical AMPscript AST.
Configs
Marketing Cloud Engagement (default)
| Config | Files | What it does |
| -------------------------- | ---------------------------- | --------------------------------------------------------- |
| sfmc.configs.ampscript | **/*.ampscript, **/*.amp | AMPscript rules only (recommended severity) |
| sfmc.configs.ssjs | **/*.ssjs | SSJS rules only (recommended severity) |
| sfmc.configs.recommended | Both of the above | All rules at recommended severity for standalone files |
| sfmc.configs.embedded | **/*.html | Combined processor extracts both languages from HTML |
| sfmc.configs.strict | All of the above + HTML | All rules at error severity for standalone and embedded |
recommended, embedded, and strict are arrays — spread them with ....
Marketing Cloud Next
Use the -next config variants when targeting Marketing Cloud Next (MCN). MCN supports only a subset of AMPscript functions and does not support SSJS at all. Handlebars is MCN's templating language, so the -next configs also lint the {{...}} helpers and {!$...} bindings extracted from HTML and standalone .hbs files (see Handlebars Rules).
| Config | Files | What it does |
| ---------------------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------- |
| sfmc.configs['ampscript-next'] | **/*.ampscript, **/*.amp | AMPscript rules + flags functions unsupported in MCN (single config object) |
| sfmc.configs['ssjs-next'] | **/*.ssjs | Flags all SSJS API calls as MCN-unsupported; all other SSJS quality rules disabled |
| sfmc.configs['recommended-next'] | Both of the above + **/*.hbs | AMPscript MCN-aware + SSJS flagged + Handlebars rules for standalone .hbs files |
| sfmc.configs['embedded-next'] | **/*.html, **/*.hbs | AMPscript MCN-aware + SSJS flagged + Handlebars rules for HTML-embedded code and .hbs |
| sfmc.configs['strict-next'] | All of the above + HTML + **/*.hbs | All AMPscript rules at error severity + MCN flag; SSJS fully flagged; Handlebars rules on |
Standalone .hbs files (VS Code's built-in Handlebars language) are treated as MCN by default — Handlebars only runs on Marketing Cloud Next, so a .hbs file is always linted with the full Handlebars rule set. This is deliberately wired only into the -next configs; the classic recommended / strict (Engagement) configs never lint .hbs.
recommended-next, embedded-next, and strict-next are arrays — spread them with ....
// eslint.config.js — targeting Marketing Cloud Next
import sfmc from 'eslint-plugin-sfmc';
export default [...sfmc.configs['recommended-next'], ...sfmc.configs['embedded-next']];AMPscript Rules (amp-*)
| Rule | Default | Description |
| ------------------------------------------------------------------------------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| sfmc/amp-no-unknown-function | error | Disallow calls to unknown AMPscript functions |
| sfmc/amp-no-mcn-unsupported | off (error in -next) | Flag AMPscript functions unavailable in the targeted MCN API version (apiVersion) |
| sfmc/amp-function-arity | error | Enforce correct argument counts |
| sfmc/amp-arg-types | error | Check that literal arguments match expected parameter types and allowed values |
| sfmc/amp-prefer-boolean-literal | warn | All catalogued boolean-like parameters accept exactly eight forms: bare true/false, numeric 1/0, or quoted "true"/"false"/"1"/"0" (either quote style; words case-insensitive); recommends and auto-fixes to bare booleans |
| sfmc/amp-set-requires-target | error | Require set to have a target variable |
| sfmc/amp-no-smart-quotes | error | Disallow smart/curly quotes in strings |
| sfmc/amp-no-var-redeclaration | warn | Disallow re-declaring a variable with var |
| sfmc/amp-no-empty-block | warn | Disallow empty %%[ ]%% blocks |
| sfmc/amp-no-loop-counter-assign | warn | Disallow assigning to the for loop counter |
| sfmc/amp-no-inline-statement | warn | Disallow statements inside inline expressions |
| sfmc/amp-no-deprecated-function | warn | Flag deprecated functions and suggest replacements |
| sfmc/amp-no-nonfunctional-function | error | Flag functions that resolve but have no working runtime invocation |
| sfmc/amp-naming-convention | warn | Enforce variable naming convention |
| sfmc/amp-no-empty-then | warn | Disallow IF with empty THEN branch |
| sfmc/amp-require-rowcount-check | warn | Require RowCount check before FOR on LookupRows |
| sfmc/amp-no-html-comment | warn | Disallow HTML comments inside AMPscript blocks |
| sfmc/amp-no-js-line-comment | warn | Disallow JS-style // line comments in AMPscript |
| sfmc/amp-no-nested-script-tag | error | Disallow <script> tags nested inside AMPscript script tags |
| sfmc/amp-no-nested-ampscript-delimiter | error | Disallow AMPscript delimiters nested inside AMPscript blocks |
| sfmc/amp-prefer-attribute-value | off | Prefer AttributeValue() over bare personalization |
| sfmc/amp-require-variable-declaration | off | Require var before set |
| sfmc/amp-no-email-excluded-function | off | Flag functions unavailable in email context |
SSJS Rules (ssjs-*)
| Rule | Default | Description |
| ------------------------------------------------------------------------------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| sfmc/ssjs-require-platform-load | error | Require Platform.Load("core") before Core or requiresCoreLoad globals |
| sfmc/ssjs-no-unsupported-syntax | error | Flag ES6+ syntax not supported by SFMC |
| sfmc/ssjs-no-unknown-function | error | Disallow unknown methods on Platform.*, HTTP, Core Library, and WSProxy |
| sfmc/ssjs-no-mcn-unsupported | off (error in -next) | Flag all SSJS API usage as unsupported in Marketing Cloud Next |
| sfmc/ssjs-no-deprecated-function | error | Flag deprecated SFMC SSJS APIs (Classic Content classes, ErrorUtil); reports a stronger "undefined at runtime" message when the loaded Core version no longer provides the API |
| sfmc/ssjs-no-nonexistent-global | error | Flag documented SSJS globals that throw ReferenceError at runtime |
| sfmc/ssjs-no-property-call | error | Disallow calling Platform.Request/Response properties as functions |
| sfmc/ssjs-no-invalid-property-access | error | Disallow reading write-only properties (postData) and assigning to read-only ones (Platform.Request.*) |
| sfmc/ssjs-no-clr-header-access | error | Disallow CLR-unsafe reads of HttpResponse.headers; read via for..in |
| sfmc/ssjs-require-string-clr-content | error | Require wrapping HttpResponse.content with String() before use |
| sfmc/ssjs-platform-function-arity | error | Enforce correct arity for Platform.Function.* |
| sfmc/ssjs-require-platform-load-order | error | Require Platform.Load() before Core usage in order |
| sfmc/ssjs-no-hardcoded-credentials | error | Flag hardcoded keys in encryption calls |
| sfmc/ssjs-cache-loop-length | warn | Require caching .length in for-loops |
| sfmc/ssjs-require-hasownproperty | warn | Require hasOwnProperty guard in for-in loops |
| sfmc/ssjs-prefer-platform-load-version | warn | Enforce a minimum Platform.Load version string; autofix is withheld in files using ErrorUtil |
| sfmc/ssjs-no-unavailable-method | warn | Flag Array/String methods unavailable or broken in SFMC's ES3 engine |
| sfmc/ssjs-no-nonfunctional-method | warn | Flag Core Library methods that never take effect at runtime |
| sfmc/ssjs-prefer-parsejson-safe-arg | warn | Require string coercion on ParseJSON argument |
| sfmc/ssjs-no-switch-default | warn | Disallow default clause in switch statements |
| sfmc/ssjs-no-switch-fallthrough | warn | Flag stacked labels and unterminated clauses that rely on switch fall-through |
| sfmc/ssjs-no-treatascontent-injection | warn | Flag dynamic string concatenation in TreatAsContent calls |
| sfmc/ssjs-core-method-arity | warn | Enforce correct argument counts for Core Library object methods |
| sfmc/ssjs-arg-types | warn | Check that literal arguments match expected parameter types |
| sfmc/ssjs-http-property-value | error | Validate literal HTTP request property values |
| sfmc/ssjs-no-invalid-property-access | error | Flag reads of write-only properties and writes to read-only properties |
Handlebars Rules (hbs-*)
Handlebars is the templating language for Marketing Cloud Next (MCN) only. These rules are enabled at error severity in the -next configs — applied both to {{...}} extracted from HTML and to standalone .hbs files — and are off in the classic (Engagement) configs — in classic SFMC, {{...}} is plain content and must not be flagged.
| Rule | Default (-next) | Description |
| --------------------------------------------------------------------------------- | ----------------- | ------------------------------------------------------------------------------------ |
| sfmc/hbs-no-unknown-helper | error | Disallow helper invocations that are not part of the MCN catalog |
| sfmc/hbs-no-unknown-binding | error | Disallow unknown {!$...} built-in data bindings |
| sfmc/hbs-helper-arity | error | Enforce correct positional-argument counts for known helpers |
| sfmc/hbs-no-unsupported-construct | error | Disallow constructs unsupported by the MCN engine (partials, decorators, log) |
| sfmc/hbs-no-mcn-unsupported | error | Flag helpers and bindings unavailable in the targeted MCN API version (apiVersion) |
MSO / Outlook email checks
Email HTML for SFMC frequently contains Outlook conditional comments (<!--[if mso]>…<![endif]-->), MSO-only CSS, and VML fallbacks. These checks are provided by eslint-plugin-mso-email, which ships as a bundled dependency of eslint-plugin-sfmc — you do not load it separately.
The MSO rules are auto-included in the HTML-embedded configs: embedded, strict, embedded-next, and strict-next. When you lint an .html file with any of these, the combined sfmc/sfmc processor also extracts MSO conditional comments and the document body and runs the MSO rules on them — no extra processor or config to wire up, and no processor conflict.
| Rule (mso/*) | Default (embedded/strict) | Description |
| -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | ----------------------------------------------------------- |
| mso/valid-mso-condition | error | Validate the [if …] expression syntax of MSO comments |
| mso/matching-mso-endif | error | Require every MSO opener to have a matching <![endif]> |
| mso/matching-mso-endif-type | warn | Require the endif comment style to match its opener |
| mso/no-unknown-mso-property | warn | Flag unknown mso-* CSS properties |
| mso/vml-requires-namespace | warn | Require the v: VML namespace declaration when VML is used |
| mso/no-unknown-vml-tag | warn | Flag unknown v:* VML tags |
| mso/no-unknown-vml-attribute | warn | Flag unknown attributes on VML tags |
| mso/table-presentation-role | warn | Require role="presentation" on layout tables |
Severities above are the same in embedded, strict, embedded-next, and strict-next (MSO markup is engine-agnostic — it behaves identically for Engagement and Next). For MSO rule details and options, see the eslint-plugin-mso-email docs.
Using eslint-plugin-unicorn with SFMC
eslint-plugin-unicorn is a high-quality, widely used plugin that we strongly recommend — but it is built for modern JavaScript, not SFMC's SSJS runtime. SFMC SSJS runs on a JINT-based ES3/ES5-era engine that lacks many built-ins (Array#includes, String#startsWith, Set, Map, Math.trunc, spread ..., ES modules, async/await, …). CloudPage probes found zero-argument string slice() throwing after a working "Hello".substring() call. Native Object.fromEntries, toReversed, toSorted, toSpliced, replaceAll, matchAll and structuredClone were unavailable: typeof returned undefined and direct calls threw, both without Core and with Core 1.1.1 or 1.1.5.
For Unicorn 76.0.0, the SSJS compatibility overrides disable 60 of its 315 enabled recommended rules: API/syntax incompatibilities, recorded runtime-semantic hazards, and diagnostic-only pressure toward unsafe changes. Some paths are conditionally inert under strict ES5 parsing. The other 255 remain active; this classification is not a blanket runtime-safety guarantee or new live SFMC verification.
Unicorn is optional; Unicorn 76 composition is upstream-supported on ESLint 10.4.0+, while the base SFMC plugin supports ESLint 10.0.0+. The higher integration floor comes from eslint-plugin-unicorn@76's declared ESLint peer range and does not raise eslint-plugin-sfmc's package-wide peer minimum. When you enable Unicorn, the matching SSJS protections are required. Include unicorn-ssjs when Unicorn covers standalone SSJS and unicorn-ssjs-embedded when it covers embedded SSJS. The example below covers both scopes, so both overrides are required. eslint-plugin-sfmc does not depend on or load unicorn. The override configs are plain rules objects with no plugins key, so they only resolve when your own unicorn config (which registers the unicorn plugin) is loaded earlier in the flat-config array. Insert each sfmc override object directly after it; only the recommended and embedded arrays are spread:
import sfmc from 'eslint-plugin-sfmc';
import eslintPluginUnicorn from 'eslint-plugin-unicorn';
export default [
{
...eslintPluginUnicorn.configs.recommended,
files: ['**/*.{js,mjs,cjs,ssjs}'],
languageOptions: { ...eslintPluginUnicorn.configs.recommended.languageOptions, globals: {} },
},
...sfmc.configs.recommended,
...sfmc.configs.embedded, // AMPscript + SSJS embedded in HTML (<script runat="server">)
sfmc.configs['unicorn-ssjs'], // REQUIRED: protect standalone SSJS from the 60 incompatible rules
sfmc.configs['unicorn-ssjs-embedded'], // REQUIRED: protect SSJS embedded in HTML
];| Config | Files | What it does |
| --------------------------------------- | ---------------- | --------------------------------------------------------- |
| sfmc.configs['unicorn-ssjs'] | **/*.ssjs | Turns off the 60 SFMC-incompatible unicorn rules for SSJS |
| sfmc.configs['unicorn-ssjs-embedded'] | **/*.html/*.js | Same 60-rule override for SSJS embedded in HTML |
Scope the complete Unicorn preset to JavaScript/SSJS, not custom AMPscript, Handlebars or MSO ASTs. Clearing its builtin map prevents modern globals leaking into SSJS; other matching configs can still supply globals. The SFMC presets do not enable general no-undef checking, and extracted HTML server blocks are linted independently.
Only 60 of unicorn's 315 enabled recommended rules are overridden — the other 255 stay active. If you don't use unicorn, omit these configs entirely. For the full rule-by-rule breakdown (with rationale and SFMC evidence links, pinned to unicorn v76.0.0), see docs/unicorn-compatibility.md.
Processors
| Processor | Purpose |
| ---------------- | ----------------------------------------------------------------- |
| sfmc/ampscript | Extract %%[ ]%%, %%= =%%, <script language="ampscript"> |
| sfmc/ssjs | Extract <script runat="server"> (non-ampscript) |
| sfmc/sfmc | Combined: extracts AMPscript, SSJS, Handlebars, and MSO from HTML |
License
MIT
