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
@@ -0,0 +1,54 @@
1
+ # `sfmc/amp-no-js-line-comment`
2
+
3
+ > Disallow JavaScript-style single-line comments (`// ...`) 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 only supports block comments (`/* ... */`). JavaScript-style single-line comments (`// comment`) are silently ignored by the SFMC compiler, but they cause crashes in ESLint (the `spaced-comment` rule throws an unhandled error), break Prettier formatting, and can confuse the language server.
14
+
15
+ Note: `://` in URLs (e.g. `"https://example.com"`) inside string literals is not flagged because string contents are not parsed as code.
16
+
17
+ ## Settings
18
+
19
+ | Setting | Values | Default |
20
+ |---------|--------|---------|
21
+ | severity | `"error"` \| `"warn"` \| `"off"` | `"warn"` |
22
+
23
+ This rule has no configuration options.
24
+
25
+ ### Examples
26
+
27
+ **Not allowed:**
28
+
29
+ ```ampscript
30
+ %%[
31
+ // ---------------Language---------------
32
+ set @Opco = AttributeValue("OwningEntity__c")
33
+ ]%%
34
+ ```
35
+
36
+ **Allowed:**
37
+
38
+ ```ampscript
39
+ %%[
40
+ /* ---------------Language--------------- */
41
+ set @Opco = AttributeValue("OwningEntity__c")
42
+ ]%%
43
+ ```
44
+
45
+ ## Fix
46
+
47
+ This rule provides an **auto-fix**: `// comment text` is replaced with `/* comment text */`.
48
+
49
+ ## When to Disable
50
+
51
+ ```js
52
+ // eslint.config.js
53
+ rules: { 'sfmc/amp-no-js-line-comment': 'off' }
54
+ ```
@@ -0,0 +1,52 @@
1
+ # `sfmc/amp-no-loop-counter-assign`
2
+
3
+ > Disallow assigning to the `for` loop counter variable inside the loop body.
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
+ AMPscript documentation explicitly states that modifying the counter variable inside a `FOR … TO … DO` loop is not allowed and causes validation or runtime errors. The counter (`@i` in `FOR @i = 1 TO @n DO`) is managed by the runtime; attempting to reassign it produces undefined behavior.
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
+ for @i = 1 to 10 do
30
+ set @i = Add(@i, 1)
31
+ next @i
32
+ ]%%
33
+ ```
34
+
35
+ **Allowed:**
36
+
37
+ ```ampscript
38
+ %%[
39
+ var @step
40
+ for @i = 1 to 10 do
41
+ set @step = Add(@i, 1)
42
+ output(v(@step))
43
+ next @i
44
+ ]%%
45
+ ```
46
+
47
+ ## When to Disable
48
+
49
+ ```js
50
+ // eslint.config.js
51
+ rules: { 'sfmc/amp-no-loop-counter-assign': 'off' }
52
+ ```
@@ -0,0 +1,69 @@
1
+ # `sfmc/amp-no-nested-ampscript-delimiter`
2
+
3
+ > Disallow `%%[` or `%%=` delimiters inside an already-open AMPscript region.
4
+
5
+ | | |
6
+ |---|---|
7
+ | **Type** | `problem` |
8
+ | **Default severity** | `error` in `recommended` and `strict` |
9
+ | **Fixable** | **Auto-fix** (`eslint --fix`) |
10
+
11
+ ## Why This Rule Exists
12
+
13
+ AMPscript delimiters (`%%[`, `]%%`, `%%=`, `=%%`) are used in HTML context to mark the start and end of AMPscript regions. Inside an already-open region — whether a `%%[...]%%` block, a `%%=...=%%` inline expression, or a `<script language="ampscript">` tag — these delimiters are not needed and indicate a structural mistake.
14
+
15
+ Two sub-cases:
16
+
17
+ - `%%[` or `%%=` inside a `<script language="ampscript">` block — redundant because the script tag already establishes AMPscript context for its entire body.
18
+ - `%%[` nested inside an already-open `%%[...]%%` block — double-opening without a corresponding close.
19
+
20
+ ## Settings
21
+
22
+ | Setting | Values | Default |
23
+ |---------|--------|---------|
24
+ | severity | `"error"` \| `"warn"` \| `"off"` | `"error"` |
25
+
26
+ This rule has no configuration options.
27
+
28
+ ### Examples
29
+
30
+ **Not allowed — delimiter inside script tag:**
31
+
32
+ ```html
33
+ <script runat="server" language="ampscript">
34
+ %%[ set @x = "redundant delimiter" ]%%
35
+ </script>
36
+ ```
37
+
38
+ **Not allowed — nested block delimiter:**
39
+
40
+ ```ampscript
41
+ %%[
42
+ %%[ set @inner = "nested" ]%%
43
+ ]%%
44
+ ```
45
+
46
+ **Allowed:**
47
+
48
+ ```html
49
+ <script runat="server" language="ampscript">
50
+ set @x = "no delimiter needed here"
51
+ </script>
52
+ ```
53
+
54
+ ```ampscript
55
+ %%[
56
+ set @x = "correct"
57
+ ]%%
58
+ ```
59
+
60
+ ## Fix
61
+
62
+ This rule provides an **auto-fix** that removes the redundant delimiter pair (`%%[`/`]%%` or `%%=`/`=%%`), leaving only the inner code.
63
+
64
+ ## When to Disable
65
+
66
+ ```js
67
+ // eslint.config.js
68
+ rules: { 'sfmc/amp-no-nested-ampscript-delimiter': 'off' }
69
+ ```
@@ -0,0 +1,58 @@
1
+ # `sfmc/amp-no-nested-script-tag`
2
+
3
+ > Disallow nested `<script language="ampscript">` tags inside an already-open AMPscript script block.
4
+
5
+ | | |
6
+ |---|---|
7
+ | **Type** | `problem` |
8
+ | **Default severity** | `error` in `recommended` and `strict` |
9
+ | **Fixable** | **Auto-fix** (`eslint --fix`) |
10
+
11
+ ## Why This Rule Exists
12
+
13
+ Each `<script runat="server" language="ampscript">` block must be closed with `</script>` before a new one can be opened. A nested opening tag is structurally invalid and almost always indicates a missing `</script>` closing tag. The SFMC compiler may silently ignore the nested tag, leading to code that appears to work but is structured incorrectly.
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
+ ```html
28
+ <script runat="server" language="ampscript">
29
+ set @a = "outer"
30
+
31
+ <script runat="server" language="ampscript">
32
+ set @b = "inner - this tag is nested!"
33
+ </script>
34
+ </script>
35
+ ```
36
+
37
+ **Allowed:**
38
+
39
+ ```html
40
+ <script runat="server" language="ampscript">
41
+ set @a = "outer"
42
+ </script>
43
+
44
+ <script runat="server" language="ampscript">
45
+ set @b = "second block - valid"
46
+ </script>
47
+ ```
48
+
49
+ ## Fix
50
+
51
+ This rule provides an **auto-fix**: inserts `</script>` immediately before the nested opening tag, converting the nested block into a valid sequential block.
52
+
53
+ ## When to Disable
54
+
55
+ ```js
56
+ // eslint.config.js
57
+ rules: { 'sfmc/amp-no-nested-script-tag': 'off' }
58
+ ```
@@ -0,0 +1,60 @@
1
+ # `sfmc/amp-no-smart-quotes`
2
+
3
+ > Disallow smart/curly quotes in string literals.
4
+
5
+ | | |
6
+ |---|---|
7
+ | **Type** | `problem` |
8
+ | **Default severity** | `error` in `recommended` and `strict` |
9
+ | **Fixable** | **Auto-fix** (`eslint --fix`) |
10
+
11
+ ## Why This Rule Exists
12
+
13
+ AMPscript's parser only recognises straight ASCII single (`'`) and double (`"`) quotes as string delimiters. Smart (curly) quotes — such as `"`, `"`, `'`, `'` — are commonly introduced by word processors, rich-text editors, or copy-pasting from documentation. When these characters appear inside a string literal the AMPscript engine produces a parse error or treats the content as malformed, causing the email or page to fail silently or crash.
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
+ var @name
30
+ set @name = "John"
31
+ set @name = 'Jane'
32
+ ]%%
33
+ ```
34
+
35
+ **Allowed:**
36
+
37
+ ```ampscript
38
+ %%[
39
+ var @name
40
+ set @name = "John"
41
+ set @name = 'Jane'
42
+ ]%%
43
+ ```
44
+
45
+ ## Fix
46
+
47
+ This rule provides an **auto-fix**. Applied by:
48
+
49
+ - `eslint --fix` on the command line (applies ALL fixable rules regardless of `meta.type`; use `--fix-type problem` or `--fix-type suggestion` to filter by rule category)
50
+ - **Fix this issue** / **Fix all auto-fixable problems** in VS Code (requires the [ESLint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint))
51
+ - On save via `editor.codeActionsOnSave: { "source.fixAll.eslint": "explicit" }`
52
+
53
+ What the fix does: replaces every smart quote inside the string content with its ASCII equivalent (`'` for single-flavour curly quotes, `"` for double-flavour). If the replacement would introduce the existing outer delimiter, the outer quotes are switched (e.g. `"…"` becomes `'…'`). If both quote types would appear after replacement, no automatic fix is applied.
54
+
55
+ ## When to Disable
56
+
57
+ ```js
58
+ // eslint.config.js
59
+ rules: { 'sfmc/amp-no-smart-quotes': 'off' }
60
+ ```
@@ -0,0 +1,50 @@
1
+ # `sfmc/amp-no-unknown-function`
2
+
3
+ > Disallow calls to function names not in the known AMPscript catalog.
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 does not support user-defined functions. Every function call must match a name in Salesforce's published catalog. Calling an unknown name causes a runtime error (or is silently ignored depending on the execution context), making the code unreliable. This rule catches typos and invented names before deployment.
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
+ var @result
30
+ set @result = CustomLookup("MyDE", "Key", @key)
31
+ ]%%
32
+ ```
33
+
34
+ **Allowed:**
35
+
36
+ ```ampscript
37
+ %%[
38
+ var @result
39
+ set @result = Lookup("MyDE", "Value", "Key", @key)
40
+ ]%%
41
+ ```
42
+
43
+ ## When to Disable
44
+
45
+ Only disable this rule if you are intentionally using a proprietary or undocumented SFMC extension that is not in the public catalog.
46
+
47
+ ```js
48
+ // eslint.config.js
49
+ rules: { 'sfmc/amp-no-unknown-function': 'off' }
50
+ ```
@@ -0,0 +1,52 @@
1
+ # `sfmc/amp-no-var-redeclaration`
2
+
3
+ > Disallow re-declaring a variable with `var` after it has already been declared.
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
+ In AMPscript, re-declaring an already-declared variable with `var` silently resets its value to `null`. This is a common source of bugs when code is copy-pasted or blocks are re-ordered — the second `var @x` statement discards any value assigned in between, leading to unexpected empty output or runtime errors downstream.
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 @name
30
+ set @name = "Alice"
31
+ var @name
32
+ /* @name is now null */
33
+ output(concat("Hello ", @name))
34
+ ]%%
35
+ ```
36
+
37
+ **Allowed:**
38
+
39
+ ```ampscript
40
+ %%[
41
+ var @name
42
+ set @name = "Alice"
43
+ output(concat("Hello ", @name))
44
+ ]%%
45
+ ```
46
+
47
+ ## When to Disable
48
+
49
+ ```js
50
+ // eslint.config.js
51
+ rules: { 'sfmc/amp-no-var-redeclaration': 'off' }
52
+ ```
@@ -0,0 +1,64 @@
1
+ # `sfmc/amp-prefer-attribute-value`
2
+
3
+ > Prefer `AttributeValue()` over bare personalization strings for safe attribute access.
4
+
5
+ | | |
6
+ |---|---|
7
+ | **Type** | `suggestion` |
8
+ | **Default severity** | `off` by default |
9
+ | **Fixable** | **Suggestion** (manual, via VS Code lightbulb) |
10
+
11
+ ## Why This Rule Exists
12
+
13
+ Using a bare identifier such as `FirstName` in a `SET` expression or as a standalone output relies on AMPscript's implicit personalization-string lookup. If the subscriber profile does not contain that attribute the send is aborted with an error. Wrapping the reference in `AttributeValue("FirstName")` instead returns an empty string (or a default value if the second argument is provided) when the attribute is missing, preventing send failures.
14
+
15
+ ## Settings
16
+
17
+ | Setting | Values | Default |
18
+ |---------|--------|---------|
19
+ | severity | `"error"` \| `"warn"` \| `"off"` | `"off"` |
20
+
21
+ This rule has no configuration options.
22
+
23
+ ### Examples
24
+
25
+ **Not allowed (when rule is enabled):**
26
+
27
+ ```ampscript
28
+ %%[
29
+ var @greeting
30
+ set @greeting = concat("Hello, ", FirstName)
31
+ ]%%
32
+ %%=FirstName=%%
33
+ ```
34
+
35
+ **Allowed:**
36
+
37
+ ```ampscript
38
+ %%[
39
+ var @greeting
40
+ set @greeting = concat("Hello, ", AttributeValue("FirstName"))
41
+ ]%%
42
+ %%=AttributeValue("FirstName")=%%
43
+
44
+ /* With a default value: */
45
+ %%=AttributeValue("FirstName", "default", "", "Subscriber")=%%
46
+ ```
47
+
48
+ ## Fix
49
+
50
+ This rule provides a **suggestion** (not applied automatically). To apply it:
51
+
52
+ - 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))
53
+ - `eslint --fix` does **not** apply suggestions (`--fix-type suggestion` filters fixable rules by rule category, it does **not** apply `hasSuggestions` suggestions)
54
+
55
+ What the suggestion does: wraps the bare identifier (e.g. `FirstName`) in `AttributeValue("FirstName")`.
56
+
57
+ ## When to Disable
58
+
59
+ This rule is off by default because bare personalization strings are valid AMPscript. Enable it only if your team wants to enforce the safer `AttributeValue()` pattern across all content.
60
+
61
+ ```js
62
+ // eslint.config.js
63
+ rules: { 'sfmc/amp-prefer-attribute-value': 'warn' }
64
+ ```
@@ -0,0 +1,62 @@
1
+ # `sfmc/amp-require-rowcount-check`
2
+
3
+ > Require a `RowCount > 0` check before iterating over a `LookupRows` result.
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
+ When `LookupRows` (or `LookupOrderedRows`) returns an empty rowset and you immediately use `FOR @i = 1 TO RowCount(@rows)`, AMPscript evaluates `RowCount(@rows)` as `0`. A `FOR` loop iterating `1 TO 0` triggers a runtime error on some SFMC versions because the loop body attempts to access row index `1` from an empty set. The safe pattern is to guard the loop with `IF RowCount(@rows) > 0 THEN … ENDIF`.
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 = LookupRows("MyDE", "Status", "Active")
31
+
32
+ for @i = 1 to RowCount(@rows) do
33
+ var @email
34
+ set @email = Field(Row(@rows, @i), "Email")
35
+ output(v(@email))
36
+ next @i
37
+ ]%%
38
+ ```
39
+
40
+ **Allowed:**
41
+
42
+ ```ampscript
43
+ %%[
44
+ var @rows
45
+ set @rows = LookupRows("MyDE", "Status", "Active")
46
+
47
+ if RowCount(@rows) > 0 then
48
+ for @i = 1 to RowCount(@rows) do
49
+ var @email
50
+ set @email = Field(Row(@rows, @i), "Email")
51
+ output(v(@email))
52
+ next @i
53
+ endif
54
+ ]%%
55
+ ```
56
+
57
+ ## When to Disable
58
+
59
+ ```js
60
+ // eslint.config.js
61
+ rules: { 'sfmc/amp-require-rowcount-check': 'off' }
62
+ ```
@@ -0,0 +1,51 @@
1
+ # `sfmc/amp-require-variable-declaration`
2
+
3
+ > Require variables to be declared with `var` before use in `set` statements.
4
+
5
+ | | |
6
+ |---|---|
7
+ | **Type** | `suggestion` |
8
+ | **Default severity** | `off` by default |
9
+ | **Fixable** | — |
10
+
11
+ ## Why This Rule Exists
12
+
13
+ AMPscript does not technically require `var` before `set` — you can write `set @x = 1` without a prior declaration. However, the [ampscript.guide](https://ampscript.guide) best-practices documentation recommends always declaring variables with `var` first. This makes code self-documenting, helps catch variable-name typos at lint time, and mirrors the conventions used in official Salesforce examples.
14
+
15
+ System variables (those starting with `@@`) are not affected.
16
+
17
+ ## Settings
18
+
19
+ | Setting | Values | Default |
20
+ |---------|--------|---------|
21
+ | severity | `"error"` \| `"warn"` \| `"off"` | `"off"` |
22
+
23
+ This rule has no configuration options.
24
+
25
+ ### Examples
26
+
27
+ **Not allowed (when rule is enabled):**
28
+
29
+ ```ampscript
30
+ %%[
31
+ set @name = "Alice"
32
+ ]%%
33
+ ```
34
+
35
+ **Allowed:**
36
+
37
+ ```ampscript
38
+ %%[
39
+ var @name
40
+ set @name = "Alice"
41
+ ]%%
42
+ ```
43
+
44
+ ## When to Disable
45
+
46
+ This rule is off by default because undeclared `set` is valid AMPscript. Enable it for teams that want to enforce a strict declaration-first style.
47
+
48
+ ```js
49
+ // eslint.config.js
50
+ rules: { 'sfmc/amp-require-variable-declaration': 'warn' }
51
+ ```
@@ -0,0 +1,47 @@
1
+ # `sfmc/amp-set-requires-target`
2
+
3
+ > Require `set` statements to have a target variable.
4
+
5
+ | | |
6
+ |---|---|
7
+ | **Type** | `problem` |
8
+ | **Default severity** | `error` in `recommended` and `strict` |
9
+ | **Fixable** | — |
10
+
11
+ ## Why This Rule Exists
12
+
13
+ The correct form of a `set` statement is `set @variable = expression`. A bare `set = expression` with no target is either a parse error or produces undefined runtime behavior. This always indicates a typo or incomplete code.
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
+ set = Lookup("MyDE", "Value", "Key", @key)
30
+ ]%%
31
+ ```
32
+
33
+ **Allowed:**
34
+
35
+ ```ampscript
36
+ %%[
37
+ var @result
38
+ set @result = Lookup("MyDE", "Value", "Key", @key)
39
+ ]%%
40
+ ```
41
+
42
+ ## When to Disable
43
+
44
+ ```js
45
+ // eslint.config.js
46
+ rules: { 'sfmc/amp-set-requires-target': 'off' }
47
+ ```
@@ -0,0 +1,59 @@
1
+ # `sfmc/ssjs-cache-loop-length`
2
+
3
+ > Require caching array `.length` in `for` loop conditions.
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
+ SFMC's SSJS engine (JINT) re-evaluates the loop test expression on every iteration. A condition like `i < arr.length` therefore calls the `.length` getter on every pass through the loop. For large arrays this is a measurable performance cost. The recommended pattern (per Mateusz Dąbrowski's SSJS style guide) is to cache the length in a variable in the loop initialiser:
14
+
15
+ ```js
16
+ for (var i = 0, _len = arr.length; i < _len; i++) { … }
17
+ ```
18
+
19
+ ## Settings
20
+
21
+ | Setting | Values | Default |
22
+ |---------|--------|---------|
23
+ | severity | `"error"` \| `"warn"` \| `"off"` | `"warn"` |
24
+
25
+ This rule has no configuration options.
26
+
27
+ ### Examples
28
+
29
+ **Not allowed:**
30
+
31
+ ```js
32
+ for (var i = 0; i < items.length; i++) {
33
+ process(items[i]);
34
+ }
35
+ ```
36
+
37
+ **Allowed:**
38
+
39
+ ```js
40
+ for (var i = 0, _len = items.length; i < _len; i++) {
41
+ process(items[i]);
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: appends `, _len = arr.length` to the last declarator in the `for` loop initialiser and replaces `arr.length` in the test condition with `_len`. The suggestion is only offered when the loop `init` is a `var` declaration (the typical case).
53
+
54
+ ## When to Disable
55
+
56
+ ```js
57
+ // eslint.config.js
58
+ rules: { 'sfmc/ssjs-cache-loop-length': 'off' }
59
+ ```