eslint-plugin-sfmc 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +100 -0
  3. package/docs/rules/amp/function-arity.md +53 -0
  4. package/docs/rules/amp/naming-convention.md +76 -0
  5. package/docs/rules/amp/no-deprecated-function.md +78 -0
  6. package/docs/rules/amp/no-email-excluded-function.md +69 -0
  7. package/docs/rules/amp/no-empty-block.md +59 -0
  8. package/docs/rules/amp/no-empty-then.md +62 -0
  9. package/docs/rules/amp/no-html-comment.md +67 -0
  10. package/docs/rules/amp/no-inline-statement.md +51 -0
  11. package/docs/rules/amp/no-js-line-comment.md +54 -0
  12. package/docs/rules/amp/no-loop-counter-assign.md +52 -0
  13. package/docs/rules/amp/no-nested-ampscript-delimiter.md +69 -0
  14. package/docs/rules/amp/no-nested-script-tag.md +58 -0
  15. package/docs/rules/amp/no-smart-quotes.md +60 -0
  16. package/docs/rules/amp/no-unknown-function.md +50 -0
  17. package/docs/rules/amp/no-var-redeclaration.md +52 -0
  18. package/docs/rules/amp/prefer-attribute-value.md +64 -0
  19. package/docs/rules/amp/require-rowcount-check.md +62 -0
  20. package/docs/rules/amp/require-variable-declaration.md +51 -0
  21. package/docs/rules/amp/set-requires-target.md +47 -0
  22. package/docs/rules/ssjs/cache-loop-length.md +59 -0
  23. package/docs/rules/ssjs/no-hardcoded-credentials.md +52 -0
  24. package/docs/rules/ssjs/no-switch-default.md +37 -0
  25. package/docs/rules/ssjs/no-treatascontent-injection.md +38 -0
  26. package/docs/rules/ssjs/no-unavailable-method.md +134 -0
  27. package/docs/rules/ssjs/no-unknown-core-method.md +46 -0
  28. package/docs/rules/ssjs/no-unknown-http-method.md +44 -0
  29. package/docs/rules/ssjs/no-unknown-platform-client-browser.md +44 -0
  30. package/docs/rules/ssjs/no-unknown-platform-function.md +44 -0
  31. package/docs/rules/ssjs/no-unknown-platform-request.md +43 -0
  32. package/docs/rules/ssjs/no-unknown-platform-response.md +43 -0
  33. package/docs/rules/ssjs/no-unknown-platform-variable.md +43 -0
  34. package/docs/rules/ssjs/no-unknown-wsproxy-method.md +46 -0
  35. package/docs/rules/ssjs/no-unsupported-syntax.md +83 -0
  36. package/docs/rules/ssjs/platform-function-arity.md +45 -0
  37. package/docs/rules/ssjs/prefer-parsejson-safe-arg.md +42 -0
  38. package/docs/rules/ssjs/prefer-platform-load-version.md +68 -0
  39. package/docs/rules/ssjs/require-hasownproperty.md +57 -0
  40. package/docs/rules/ssjs/require-platform-load-order.md +46 -0
  41. package/docs/rules/ssjs/require-platform-load.md +57 -0
  42. package/package.json +52 -0
  43. package/src/ampscript-parser.js +159 -0
  44. package/src/ampscript-processor.js +132 -0
  45. package/src/index.js +355 -0
  46. package/src/processor.js +147 -0
  47. package/src/rules/amp/function-arity.js +51 -0
  48. package/src/rules/amp/naming-convention.js +58 -0
  49. package/src/rules/amp/no-deprecated-function.js +75 -0
  50. package/src/rules/amp/no-email-excluded-function.js +72 -0
  51. package/src/rules/amp/no-empty-block.js +37 -0
  52. package/src/rules/amp/no-empty-then.js +34 -0
  53. package/src/rules/amp/no-html-comment.js +57 -0
  54. package/src/rules/amp/no-inline-statement.js +45 -0
  55. package/src/rules/amp/no-js-line-comment.js +54 -0
  56. package/src/rules/amp/no-loop-counter-assign.js +71 -0
  57. package/src/rules/amp/no-nested-ampscript-delimiter.js +102 -0
  58. package/src/rules/amp/no-nested-script-tag.js +65 -0
  59. package/src/rules/amp/no-smart-quotes.js +78 -0
  60. package/src/rules/amp/no-unknown-function.js +31 -0
  61. package/src/rules/amp/no-var-redeclaration.js +36 -0
  62. package/src/rules/amp/prefer-attribute-value.js +94 -0
  63. package/src/rules/amp/require-rowcount-check.js +116 -0
  64. package/src/rules/amp/require-variable-declaration.js +49 -0
  65. package/src/rules/amp/set-requires-target.js +27 -0
  66. package/src/rules/ssjs/cache-loop-length.js +92 -0
  67. package/src/rules/ssjs/no-hardcoded-credentials.js +66 -0
  68. package/src/rules/ssjs/no-switch-default.js +32 -0
  69. package/src/rules/ssjs/no-treatascontent-injection.js +76 -0
  70. package/src/rules/ssjs/no-unavailable-method.js +190 -0
  71. package/src/rules/ssjs/no-unknown-core-method.js +103 -0
  72. package/src/rules/ssjs/no-unknown-http-method.js +41 -0
  73. package/src/rules/ssjs/no-unknown-platform-client-browser.js +50 -0
  74. package/src/rules/ssjs/no-unknown-platform-function.js +49 -0
  75. package/src/rules/ssjs/no-unknown-platform-request.js +50 -0
  76. package/src/rules/ssjs/no-unknown-platform-response.js +50 -0
  77. package/src/rules/ssjs/no-unknown-platform-variable.js +50 -0
  78. package/src/rules/ssjs/no-unknown-wsproxy-method.js +71 -0
  79. package/src/rules/ssjs/no-unsupported-syntax.js +122 -0
  80. package/src/rules/ssjs/platform-function-arity.js +70 -0
  81. package/src/rules/ssjs/prefer-parsejson-safe-arg.js +94 -0
  82. package/src/rules/ssjs/prefer-platform-load-version.js +96 -0
  83. package/src/rules/ssjs/require-hasownproperty.js +142 -0
  84. package/src/rules/ssjs/require-platform-load-order.js +112 -0
  85. package/src/rules/ssjs/require-platform-load.js +124 -0
  86. package/src/ssjs-processor.js +48 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jörn Berkefeld
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,100 @@
1
+ # eslint-plugin-sfmc
2
+
3
+ Unified ESLint plugin for **Salesforce Marketing Cloud** — linting rules for both **AMPscript** and **Server-Side JavaScript (SSJS)**.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install eslint-plugin-sfmc --save-dev
9
+ ```
10
+
11
+ Requires ESLint 9+ (flat config).
12
+
13
+ ## Quick Start
14
+
15
+ ```js
16
+ // eslint.config.js
17
+ import sfmc from 'eslint-plugin-sfmc';
18
+
19
+ export default [
20
+ // Lint standalone .ampscript/.amp and .ssjs files
21
+ ...sfmc.configs.recommended,
22
+
23
+ // Lint AMPscript + SSJS embedded in .html files
24
+ ...sfmc.configs.embedded,
25
+ ];
26
+ ```
27
+
28
+ ## Configs
29
+
30
+ | Config | Files | What it does |
31
+ | -------------------------- | ---------------------------- | --------------------------------------------------------- |
32
+ | `sfmc.configs.ampscript` | `**/*.ampscript`, `**/*.amp` | AMPscript rules only (recommended severity) |
33
+ | `sfmc.configs.ssjs` | `**/*.ssjs` | SSJS rules only (recommended severity) |
34
+ | `sfmc.configs.recommended` | Both of the above | All rules at recommended severity for standalone files |
35
+ | `sfmc.configs.embedded` | `**/*.html` | Combined processor extracts both languages from HTML |
36
+ | `sfmc.configs.strict` | All of the above + HTML | All rules at `error` severity for standalone and embedded |
37
+
38
+ `recommended`, `embedded`, and `strict` are arrays — spread them with `...`.
39
+
40
+ ## AMPscript Rules (`amp-*`)
41
+
42
+ | Rule | Default | Description |
43
+ | --------------------------------------- | ------- | --------------------------------------------------- |
44
+ | [`sfmc/amp-no-unknown-function`](docs/rules/amp/no-unknown-function.md) | `error` | Disallow calls to unknown AMPscript functions |
45
+ | [`sfmc/amp-function-arity`](docs/rules/amp/function-arity.md) | `error` | Enforce correct argument counts |
46
+ | [`sfmc/amp-set-requires-target`](docs/rules/amp/set-requires-target.md) | `error` | Require `set` to have a target variable |
47
+ | [`sfmc/amp-no-smart-quotes`](docs/rules/amp/no-smart-quotes.md) | `error` | Disallow smart/curly quotes in strings |
48
+ | [`sfmc/amp-no-var-redeclaration`](docs/rules/amp/no-var-redeclaration.md) | `warn` | Disallow re-declaring a variable with `var` |
49
+ | [`sfmc/amp-no-empty-block`](docs/rules/amp/no-empty-block.md) | `warn` | Disallow empty `%%[ ]%%` blocks |
50
+ | [`sfmc/amp-no-loop-counter-assign`](docs/rules/amp/no-loop-counter-assign.md) | `warn` | Disallow assigning to the `for` loop counter |
51
+ | [`sfmc/amp-no-inline-statement`](docs/rules/amp/no-inline-statement.md) | `warn` | Disallow statements inside inline expressions |
52
+ | [`sfmc/amp-no-deprecated-function`](docs/rules/amp/no-deprecated-function.md) | `warn` | Flag deprecated functions and suggest replacements |
53
+ | [`sfmc/amp-naming-convention`](docs/rules/amp/naming-convention.md) | `warn` | Enforce variable naming convention |
54
+ | [`sfmc/amp-no-empty-then`](docs/rules/amp/no-empty-then.md) | `warn` | Disallow IF with empty THEN branch |
55
+ | [`sfmc/amp-require-rowcount-check`](docs/rules/amp/require-rowcount-check.md) | `warn` | Require RowCount check before FOR on LookupRows |
56
+ | [`sfmc/amp-no-html-comment`](docs/rules/amp/no-html-comment.md) | `warn` | Disallow HTML comments inside AMPscript blocks |
57
+ | [`sfmc/amp-no-js-line-comment`](docs/rules/amp/no-js-line-comment.md) | `warn` | Disallow JS-style `//` line comments in AMPscript |
58
+ | [`sfmc/amp-no-nested-script-tag`](docs/rules/amp/no-nested-script-tag.md) | `error` | Disallow `<script>` tags nested inside AMPscript script tags |
59
+ | [`sfmc/amp-no-nested-ampscript-delimiter`](docs/rules/amp/no-nested-ampscript-delimiter.md) | `error` | Disallow AMPscript delimiters nested inside AMPscript blocks |
60
+ | [`sfmc/amp-prefer-attribute-value`](docs/rules/amp/prefer-attribute-value.md) | off | Prefer `AttributeValue()` over bare personalization |
61
+ | [`sfmc/amp-require-variable-declaration`](docs/rules/amp/require-variable-declaration.md) | off | Require `var` before `set` |
62
+ | [`sfmc/amp-no-email-excluded-function`](docs/rules/amp/no-email-excluded-function.md) | off | Flag functions unavailable in email context |
63
+
64
+ ## SSJS Rules (`ssjs-*`)
65
+
66
+ | Rule | Default | Description |
67
+ | ---------------------------------------- | ------- | ---------------------------------------------------- |
68
+ | [`sfmc/ssjs-require-platform-load`](docs/rules/ssjs/require-platform-load.md) | `error` | Require `Platform.Load("core")` before Core usage |
69
+ | [`sfmc/ssjs-no-unsupported-syntax`](docs/rules/ssjs/no-unsupported-syntax.md) | `error` | Flag ES6+ syntax not supported by SFMC |
70
+ | [`sfmc/ssjs-no-unknown-platform-function`](docs/rules/ssjs/no-unknown-platform-function.md) | `error` | Disallow unknown `Platform.Function.*` methods |
71
+ | [`sfmc/ssjs-platform-function-arity`](docs/rules/ssjs/platform-function-arity.md) | `error` | Enforce correct arity for `Platform.Function.*` |
72
+ | [`sfmc/ssjs-no-unknown-http-method`](docs/rules/ssjs/no-unknown-http-method.md) | `error` | Disallow unknown `HTTP.*` methods |
73
+ | [`sfmc/ssjs-no-unknown-platform-variable`](docs/rules/ssjs/no-unknown-platform-variable.md) | `error` | Disallow unknown `Platform.Variable.*` methods |
74
+ | [`sfmc/ssjs-no-unknown-platform-response`](docs/rules/ssjs/no-unknown-platform-response.md) | `error` | Disallow unknown `Platform.Response.*` methods |
75
+ | [`sfmc/ssjs-no-unknown-platform-request`](docs/rules/ssjs/no-unknown-platform-request.md) | `error` | Disallow unknown `Platform.Request.*` methods |
76
+ | [`sfmc/ssjs-require-platform-load-order`](docs/rules/ssjs/require-platform-load-order.md) | `error` | Require `Platform.Load()` before Core usage in order |
77
+ | [`sfmc/ssjs-no-hardcoded-credentials`](docs/rules/ssjs/no-hardcoded-credentials.md) | `error` | Flag hardcoded keys in encryption calls |
78
+ | [`sfmc/ssjs-no-unknown-platform-client-browser`](docs/rules/ssjs/no-unknown-platform-client-browser.md) | `error` | Disallow unknown `Platform.ClientBrowser.*` methods |
79
+ | [`sfmc/ssjs-no-unknown-core-method`](docs/rules/ssjs/no-unknown-core-method.md) | `warn` | Disallow unknown methods on Core library objects |
80
+ | [`sfmc/ssjs-no-unknown-wsproxy-method`](docs/rules/ssjs/no-unknown-wsproxy-method.md) | `warn` | Disallow unknown WSProxy methods |
81
+ | [`sfmc/ssjs-cache-loop-length`](docs/rules/ssjs/cache-loop-length.md) | `warn` | Require caching `.length` in for-loops |
82
+ | [`sfmc/ssjs-require-hasownproperty`](docs/rules/ssjs/require-hasownproperty.md) | `warn` | Require `hasOwnProperty` guard in for-in loops |
83
+ | [`sfmc/ssjs-prefer-platform-load-version`](docs/rules/ssjs/prefer-platform-load-version.md) | `warn` | Enforce a minimum `Platform.Load` version string |
84
+ | [`sfmc/ssjs-no-unavailable-method`](docs/rules/ssjs/no-unavailable-method.md) | `warn` | Flag Array/String methods unavailable or broken in SFMC's ES3 engine |
85
+ | [`sfmc/ssjs-prefer-parsejson-safe-arg`](docs/rules/ssjs/prefer-parsejson-safe-arg.md) | `warn` | Require string coercion on `ParseJSON` argument |
86
+ | [`sfmc/ssjs-no-switch-default`](docs/rules/ssjs/no-switch-default.md) | `warn` | Disallow `default` clause in `switch` statements |
87
+ | [`sfmc/ssjs-no-treatascontent-injection`](docs/rules/ssjs/no-treatascontent-injection.md) | `warn` | Flag dynamic string concatenation in `TreatAsContent` calls |
88
+
89
+ ## Processors
90
+
91
+ | Processor | Purpose |
92
+ | ---------------- | ------------------------------------------------------------- |
93
+ | `sfmc/ampscript` | Extract `%%[ ]%%`, `%%= =%%`, `<script language="ampscript">` |
94
+ | `sfmc/ssjs` | Extract `<script runat="server">` (non-ampscript) |
95
+ | `sfmc/sfmc` | Combined: extracts both AMPscript and SSJS from HTML |
96
+
97
+
98
+ ## License
99
+
100
+ MIT
@@ -0,0 +1,53 @@
1
+ # `sfmc/amp-function-arity`
2
+
3
+ > Enforce correct argument counts for known AMPscript functions.
4
+
5
+ | | |
6
+ |---|---|
7
+ | **Type** | `problem` |
8
+ | **Default severity** | `error` in `recommended` and `strict` |
9
+ | **Fixable** | — |
10
+
11
+ ## Why This Rule Exists
12
+
13
+ AMPscript functions have fixed required and optional argument counts documented in the Salesforce catalog. Calling a function with too few arguments causes a runtime error. Calling it with too many can silently truncate or fail. This rule validates every call against the known min/max argument counts.
14
+
15
+ ## Settings
16
+
17
+ | Setting | Values | Default |
18
+ |---------|--------|---------|
19
+ | severity | `"error"` \| `"warn"` \| `"off"` | `"error"` |
20
+
21
+ This rule has no configuration options.
22
+
23
+ ### Examples
24
+
25
+ **Not allowed:**
26
+
27
+ ```ampscript
28
+ %%[
29
+ /* Lookup requires at least 4 arguments */
30
+ var @val
31
+ set @val = Lookup("MyDE", "Value")
32
+
33
+ /* Format accepts at most 3 arguments */
34
+ set @val = Format(@num, "0.00", "en-US", "extra")
35
+ ]%%
36
+ ```
37
+
38
+ **Allowed:**
39
+
40
+ ```ampscript
41
+ %%[
42
+ var @val
43
+ set @val = Lookup("MyDE", "Value", "Key", @key)
44
+ set @val = Format(@num, "0.00", "en-US")
45
+ ]%%
46
+ ```
47
+
48
+ ## When to Disable
49
+
50
+ ```js
51
+ // eslint.config.js
52
+ rules: { 'sfmc/amp-function-arity': 'off' }
53
+ ```
@@ -0,0 +1,76 @@
1
+ # `sfmc/amp-naming-convention`
2
+
3
+ > Enforce a consistent naming convention for `@variable` names.
4
+
5
+ | | |
6
+ |---|---|
7
+ | **Type** | `suggestion` |
8
+ | **Default severity** | `off` by default; `warn` when explicitly enabled |
9
+ | **Fixable** | — |
10
+
11
+ ## Why This Rule Exists
12
+
13
+ AMPscript variable names are case-insensitive at runtime, but inconsistent casing (`@myVar` vs `@MyVar` vs `@MYVAR` for the same variable) causes confusion during code reviews and maintenance. Enforcing a single convention across a codebase makes intent clearer and reduces the risk of typos that create an unintended second variable reference.
14
+
15
+ ## Settings
16
+
17
+ | Setting | Values | Default |
18
+ |---------|--------|---------|
19
+ | severity | `"error"` \| `"warn"` \| `"off"` | `"off"` |
20
+ | `format` | `"camelCase"` \| `"PascalCase"` | `"camelCase"` |
21
+
22
+ ### Under `"camelCase"` (default)
23
+
24
+ **Not allowed:**
25
+
26
+ ```ampscript
27
+ %%[
28
+ var @FirstName
29
+ var @LAST_NAME
30
+ ]%%
31
+ ```
32
+
33
+ **Allowed:**
34
+
35
+ ```ampscript
36
+ %%[
37
+ var @firstName
38
+ var @lastName
39
+ ]%%
40
+ ```
41
+
42
+ ### Under `"PascalCase"`
43
+
44
+ **Not allowed:**
45
+
46
+ ```ampscript
47
+ %%[
48
+ var @firstName
49
+ var @last_name
50
+ ]%%
51
+ ```
52
+
53
+ **Allowed:**
54
+
55
+ ```ampscript
56
+ %%[
57
+ var @FirstName
58
+ var @LastName
59
+ ]%%
60
+ ```
61
+
62
+ ## Configuration Example
63
+
64
+ ```js
65
+ // eslint.config.js
66
+ rules: {
67
+ 'sfmc/amp-naming-convention': ['warn', { format: 'PascalCase' }]
68
+ }
69
+ ```
70
+
71
+ ## When to Disable
72
+
73
+ ```js
74
+ // eslint.config.js
75
+ rules: { 'sfmc/amp-naming-convention': 'off' }
76
+ ```
@@ -0,0 +1,78 @@
1
+ # `sfmc/amp-no-deprecated-function`
2
+
3
+ > Flag deprecated AMPscript functions and suggest their modern replacements.
4
+
5
+ | | |
6
+ |---|---|
7
+ | **Type** | `suggestion` |
8
+ | **Default severity** | `warn` in `recommended`; `error` in `strict` |
9
+ | **Fixable** | **Auto-fix** for 1:1 replacements; **Suggestion** for ambiguous replacements |
10
+
11
+ ## Why This Rule Exists
12
+
13
+ Salesforce has deprecated several AMPscript functions in favour of newer equivalents. The old functions (e.g. `InsertDE`, `UpdateDE`, `DeleteDE`, `LookupValue`) remain functional for backward compatibility but may be removed in a future platform update. `ContentArea` is a Classic-only function with no equivalent in Content Builder — callers must choose between `ContentBlockByKey` or `ContentBlockByName` depending on their setup.
14
+
15
+ ## Settings
16
+
17
+ | Setting | Values | Default |
18
+ |---------|--------|---------|
19
+ | severity | `"error"` \| `"warn"` \| `"off"` | `"warn"` |
20
+
21
+ This rule has no configuration options.
22
+
23
+ ### Examples
24
+
25
+ **Not allowed:**
26
+
27
+ ```ampscript
28
+ %%[
29
+ var @rows
30
+ set @rows = LookupValue("MyDE", "Email", "SubKey", @key)
31
+
32
+ var @html
33
+ set @html = ContentArea(42)
34
+ ]%%
35
+ ```
36
+
37
+ **Allowed:**
38
+
39
+ ```ampscript
40
+ %%[
41
+ var @rows
42
+ set @rows = Lookup("MyDE", "Email", "SubKey", @key)
43
+
44
+ /* Choose one depending on your setup: */
45
+ var @html
46
+ set @html = ContentBlockByKey("my-block-key")
47
+ /* or */
48
+ set @html = ContentBlockByName("My Block Name")
49
+ ]%%
50
+ ```
51
+
52
+ ## Fix
53
+
54
+ This rule provides two types of fix depending on the deprecated function:
55
+
56
+ **Auto-fix** (for 1:1 replacements such as `InsertDE → InsertData`, `LookupValue → Lookup`, etc.). Applied by:
57
+
58
+ - `eslint --fix` on the command line (because `meta.type` is `"suggestion"`, you can also target it specifically with `--fix-type suggestion`)
59
+ - **Fix this issue** / **Fix all auto-fixable problems** in VS Code (requires the [ESLint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint))
60
+ - On save via `editor.codeActionsOnSave: { "source.fixAll.eslint": "explicit" }`
61
+
62
+ What the auto-fix does: replaces only the function name token, leaving all arguments intact.
63
+
64
+ ---
65
+
66
+ **Suggestion** (for `ContentArea`, which maps to either `ContentBlockByKey` or `ContentBlockByName`). Applied by:
67
+
68
+ - Click the **lightbulb** / press `Ctrl+.` on the flagged code in VS Code (requires the ESLint extension)
69
+ - `eslint --fix` does **not** apply suggestions (`--fix-type suggestion` filters fixable rules by rule category, it does **not** apply `hasSuggestions` suggestions)
70
+
71
+ What the suggestion does: offers two separate options — replace `ContentArea` with `ContentBlockByKey` or with `ContentBlockByName`.
72
+
73
+ ## When to Disable
74
+
75
+ ```js
76
+ // eslint.config.js
77
+ rules: { 'sfmc/amp-no-deprecated-function': 'off' }
78
+ ```
@@ -0,0 +1,69 @@
1
+ # `sfmc/amp-no-email-excluded-function`
2
+
3
+ > Disallow AMPscript functions that are not available in the email execution context.
4
+
5
+ | | |
6
+ |---|---|
7
+ | **Type** | `problem` |
8
+ | **Default severity** | `off` by default |
9
+ | **Fixable** | — |
10
+
11
+ ## Why This Rule Exists
12
+
13
+ Several AMPscript functions — including `InsertData`, `UpdateData`, `DeleteData`, `UpsertData`, `Redirect`, `HTTPGet`, and others — are only available in CloudPage and landing-page contexts. Calling them inside an email causes a runtime error, silently fails, or aborts the send. This rule flags those calls when the file is identified as an email template.
14
+
15
+ ## Settings
16
+
17
+ | Setting | Values | Default |
18
+ |---------|--------|---------|
19
+ | severity | `"error"` \| `"warn"` \| `"off"` | `"off"` |
20
+ | `context` | `"email"` \| `"cloudpage"` \| `"auto"` | `"email"` |
21
+
22
+ ### Under `"email"` (default)
23
+
24
+ Flags every call to an email-excluded function regardless of the filename.
25
+
26
+ **Not allowed:**
27
+
28
+ ```ampscript
29
+ %%[
30
+ InsertData("MyDE", "Email", @email)
31
+ Redirect("https://example.com")
32
+ ]%%
33
+ ```
34
+
35
+ **Allowed:**
36
+
37
+ ```ampscript
38
+ %%[
39
+ InsertDE("MyDE", "Email", @email)
40
+ ]%%
41
+ ```
42
+
43
+ > Note: `InsertDE` is itself deprecated — see [`amp-no-deprecated-function`](no-deprecated-function.md).
44
+
45
+ ### Under `"cloudpage"`
46
+
47
+ Disables all checks. Use this setting for files that are exclusively used as CloudPages.
48
+
49
+ ### Under `"auto"`
50
+
51
+ Skips checking for files whose name or path contains `cloudpage` or `landing`, or whose extension is `.html`. Otherwise behaves like `"email"`.
52
+
53
+ ## Configuration Example
54
+
55
+ ```js
56
+ // eslint.config.js
57
+ rules: {
58
+ 'sfmc/amp-no-email-excluded-function': ['warn', { context: 'auto' }]
59
+ }
60
+ ```
61
+
62
+ ## When to Disable
63
+
64
+ This rule is off by default because many projects contain both email and CloudPage AMPscript. Enable it with `"context": "auto"` for mixed-use projects, or with `"context": "email"` for email-only repositories.
65
+
66
+ ```js
67
+ // eslint.config.js
68
+ rules: { 'sfmc/amp-no-email-excluded-function': 'off' }
69
+ ```
@@ -0,0 +1,59 @@
1
+ # `sfmc/amp-no-empty-block`
2
+
3
+ > Disallow empty AMPscript blocks that produce no output or side effects.
4
+
5
+ | | |
6
+ |---|---|
7
+ | **Type** | `suggestion` |
8
+ | **Default severity** | `warn` in `recommended`; `error` in `strict` |
9
+ | **Fixable** | **Suggestion** (manual, via VS Code lightbulb) |
10
+
11
+ ## Why This Rule Exists
12
+
13
+ An empty `%%[ ]%%` block (one that contains no statements other than comments) does nothing. Such blocks are typically leftovers from deleted code or copy-paste errors. While harmless at runtime, they add noise to templates and can cause confusion during maintenance.
14
+
15
+ ## Settings
16
+
17
+ | Setting | Values | Default |
18
+ |---------|--------|---------|
19
+ | severity | `"error"` \| `"warn"` \| `"off"` | `"warn"` |
20
+
21
+ This rule has no configuration options.
22
+
23
+ ### Examples
24
+
25
+ **Not allowed:**
26
+
27
+ ```ampscript
28
+ %%[
29
+ ]%%
30
+
31
+ %%[
32
+ /* TODO: add logic here */
33
+ ]%%
34
+ ```
35
+
36
+ **Allowed:**
37
+
38
+ ```ampscript
39
+ %%[
40
+ var @name
41
+ set @name = "Alice"
42
+ ]%%
43
+ ```
44
+
45
+ ## Fix
46
+
47
+ This rule provides a **suggestion** (not applied automatically). To apply it:
48
+
49
+ - Click the **lightbulb** / press `Ctrl+.` on the flagged code in VS Code (requires the [ESLint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint))
50
+ - `eslint --fix` does **not** apply suggestions (`--fix-type suggestion` filters fixable rules by rule category, it does **not** apply `hasSuggestions` suggestions)
51
+
52
+ What the suggestion does: removes the entire `%%[ ]%%` block from the source.
53
+
54
+ ## When to Disable
55
+
56
+ ```js
57
+ // eslint.config.js
58
+ rules: { 'sfmc/amp-no-empty-block': 'off' }
59
+ ```
@@ -0,0 +1,62 @@
1
+ # `sfmc/amp-no-empty-then`
2
+
3
+ > Disallow `IF` statements with an empty `THEN` branch.
4
+
5
+ | | |
6
+ |---|---|
7
+ | **Type** | `problem` |
8
+ | **Default severity** | `warn` in `recommended`; `error` in `strict` |
9
+ | **Fixable** | — |
10
+
11
+ ## Why This Rule Exists
12
+
13
+ An `IF condition THEN … ELSE … ENDIF` where the `THEN` branch is empty is almost always a logic error. The most common cause is an inverted condition — the developer wrote the consequent code in the `ELSE` branch and left `THEN` empty, or accidentally deleted the `THEN` body during editing. The code either does nothing when the condition is true, or produces the wrong output.
14
+
15
+ ## Settings
16
+
17
+ | Setting | Values | Default |
18
+ |---------|--------|---------|
19
+ | severity | `"error"` \| `"warn"` \| `"off"` | `"warn"` |
20
+
21
+ This rule has no configuration options.
22
+
23
+ ### Examples
24
+
25
+ **Not allowed:**
26
+
27
+ ```ampscript
28
+ %%[
29
+ if @isActive == 1 then
30
+ else
31
+ set @msg = "Inactive"
32
+ endif
33
+ ]%%
34
+ ```
35
+
36
+ **Allowed:**
37
+
38
+ ```ampscript
39
+ %%[
40
+ if @isActive == 1 then
41
+ set @msg = "Active"
42
+ else
43
+ set @msg = "Inactive"
44
+ endif
45
+ ]%%
46
+ ```
47
+
48
+ ```ampscript
49
+ %%[
50
+ /* Empty THEN with no ELSE is still flagged — use a positive condition instead */
51
+ if @isActive != 1 then
52
+ set @msg = "Inactive"
53
+ endif
54
+ ]%%
55
+ ```
56
+
57
+ ## When to Disable
58
+
59
+ ```js
60
+ // eslint.config.js
61
+ rules: { 'sfmc/amp-no-empty-then': 'off' }
62
+ ```
@@ -0,0 +1,67 @@
1
+ # `sfmc/amp-no-html-comment`
2
+
3
+ > Disallow HTML comment syntax (`<!-- ... -->`) inside AMPscript regions.
4
+
5
+ | | |
6
+ |---|---|
7
+ | **Type** | `problem` |
8
+ | **Default severity** | `warn` in `recommended`, `error` in `strict` |
9
+ | **Fixable** | **Auto-fix** (`eslint --fix`) |
10
+
11
+ ## Why This Rule Exists
12
+
13
+ AMPscript has no HTML comment syntax. HTML comments (`<!-- -->`) inside AMPscript blocks are silently ignored by the SFMC compiler, but they break language servers, formatters (Prettier), and ESLint — including causing crashes in ESLint's `spaced-comment` rule.
14
+
15
+ Two common variants are caught:
16
+
17
+ - `<!--/* comment */-->` — an AMPscript `/* */` block comment unnecessarily wrapped in HTML comment markers.
18
+ - `<!-- comment -->` — a plain HTML comment used where an AMPscript comment should be.
19
+
20
+ ## Settings
21
+
22
+ | Setting | Values | Default |
23
+ |---------|--------|---------|
24
+ | severity | `"error"` \| `"warn"` \| `"off"` | `"warn"` |
25
+
26
+ This rule has no configuration options.
27
+
28
+ ### Examples
29
+
30
+ **Not allowed:**
31
+
32
+ ```ampscript
33
+ %%[
34
+ <!--/* OPCO specific Language */-->
35
+ set @Opco = AttributeValue("OwningEntity__c")
36
+ ]%%
37
+ ```
38
+
39
+ ```ampscript
40
+ %%[
41
+ <!-- some section header -->
42
+ set @x = 1
43
+ ]%%
44
+ ```
45
+
46
+ **Allowed:**
47
+
48
+ ```ampscript
49
+ %%[
50
+ /* OPCO specific Language */
51
+ set @Opco = AttributeValue("OwningEntity__c")
52
+ ]%%
53
+ ```
54
+
55
+ ## Fix
56
+
57
+ This rule provides an **auto-fix**:
58
+
59
+ - `<!--/* ... */-->` → `/* ... */` (HTML wrapper is stripped, inner block comment is kept as-is)
60
+ - `<!-- ... -->` → `/* ... */` (entire construct is replaced with an AMPscript block comment)
61
+
62
+ ## When to Disable
63
+
64
+ ```js
65
+ // eslint.config.js
66
+ rules: { 'sfmc/amp-no-html-comment': 'off' }
67
+ ```
@@ -0,0 +1,51 @@
1
+ # `sfmc/amp-no-inline-statement`
2
+
3
+ > Disallow control-flow statements inside inline expressions (`%%=…=%%`).
4
+
5
+ | | |
6
+ |---|---|
7
+ | **Type** | `suggestion` |
8
+ | **Default severity** | `warn` in `recommended`; `error` in `strict` |
9
+ | **Fixable** | — |
10
+
11
+ ## Why This Rule Exists
12
+
13
+ Inline expressions (`%%=…=%%`) are designed to output a single value. Placing `IF`, `FOR`, `SET`, or `VAR` statements inside them is not supported by the AMPscript parser and causes a parse or runtime error. These constructs belong in block AMPscript (`%%[…]%%`).
14
+
15
+ ## Settings
16
+
17
+ | Setting | Values | Default |
18
+ |---------|--------|---------|
19
+ | severity | `"error"` \| `"warn"` \| `"off"` | `"warn"` |
20
+
21
+ This rule has no configuration options.
22
+
23
+ ### Examples
24
+
25
+ **Not allowed:**
26
+
27
+ ```ampscript
28
+ %%=IF(@x == 1) THEN "yes" ELSE "no" ENDIF=%%
29
+
30
+ %%=SET @name = "Alice"=%%
31
+ ```
32
+
33
+ **Allowed:**
34
+
35
+ ```ampscript
36
+ %%[
37
+ if @x == 1 then
38
+ set @out = "yes"
39
+ else
40
+ set @out = "no"
41
+ endif
42
+ ]%%
43
+ %%=v(@out)=%%
44
+ ```
45
+
46
+ ## When to Disable
47
+
48
+ ```js
49
+ // eslint.config.js
50
+ rules: { 'sfmc/amp-no-inline-statement': 'off' }
51
+ ```