eslint-plugin-sfmc 2.1.1 → 2.3.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.
- package/README.md +1 -0
- package/docs/rules/amp/arg-types.md +61 -0
- package/package.json +3 -3
- package/src/index.js +4 -0
- package/src/rules/amp/arg-types.js +91 -0
package/README.md
CHANGED
|
@@ -100,6 +100,7 @@ export default [
|
|
|
100
100
|
| ------------------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------ |
|
|
101
101
|
| [`sfmc/amp-no-unknown-function`](docs/rules/amp/no-unknown-function.md) | `error` | Disallow calls to unknown AMPscript functions |
|
|
102
102
|
| [`sfmc/amp-function-arity`](docs/rules/amp/function-arity.md) | `error` | Enforce correct argument counts |
|
|
103
|
+
| [`sfmc/amp-arg-types`](docs/rules/amp/arg-types.md) | `error` | Check that literal arguments match expected parameter types and allowed values |
|
|
103
104
|
| [`sfmc/amp-set-requires-target`](docs/rules/amp/set-requires-target.md) | `error` | Require `set` to have a target variable |
|
|
104
105
|
| [`sfmc/amp-no-smart-quotes`](docs/rules/amp/no-smart-quotes.md) | `error` | Disallow smart/curly quotes in strings |
|
|
105
106
|
| [`sfmc/amp-no-var-redeclaration`](docs/rules/amp/no-var-redeclaration.md) | `warn` | Disallow re-declaring a variable with `var` |
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# `sfmc/amp-arg-types`
|
|
2
|
+
|
|
3
|
+
> Check that literal arguments match the expected parameter types and allowed values for AMPscript functions.
|
|
4
|
+
|
|
5
|
+
| | |
|
|
6
|
+
|---|---|
|
|
7
|
+
| **Type** | `suggestion` |
|
|
8
|
+
| **Default severity** | `error` in `recommended` and `strict` |
|
|
9
|
+
| **Fixable** | — |
|
|
10
|
+
|
|
11
|
+
## Why This Rule Exists
|
|
12
|
+
|
|
13
|
+
Some AMPscript function parameters only accept a fixed set of keyword values. For example, the second argument of `DatePart` must be one of `year`, `Y`, `month`, `M`, `monthName`, `day`, `D`, `hour`, `H`, `minute`, or `MI`. Passing any other value is a bug that surfaces only at send/render time.
|
|
14
|
+
|
|
15
|
+
The Salesforce catalog records these allowed values as an `enum` on the parameter. This rule checks every static literal argument — strings, numbers, and booleans — against the parameter's `enum` (case-insensitive) and reports a mismatch. Variables and expressions are skipped because their value cannot be determined statically.
|
|
16
|
+
|
|
17
|
+
This rule is the AMPscript counterpart of [`sfmc/ssjs-arg-types`](../ssjs/arg-types.md) and may be expanded later to cover additional argument-type checks.
|
|
18
|
+
|
|
19
|
+
## Settings
|
|
20
|
+
|
|
21
|
+
| Setting | Values | Default |
|
|
22
|
+
|---------|--------|---------|
|
|
23
|
+
| severity | `"error"` \| `"warn"` \| `"off"` | `"error"` |
|
|
24
|
+
|
|
25
|
+
This rule has no configuration options.
|
|
26
|
+
|
|
27
|
+
### Examples
|
|
28
|
+
|
|
29
|
+
**Not allowed:**
|
|
30
|
+
|
|
31
|
+
```ampscript
|
|
32
|
+
%%[
|
|
33
|
+
/* 'decade' is not a valid datePart */
|
|
34
|
+
set @x = DatePart('2026-01-15', 'decade')
|
|
35
|
+
|
|
36
|
+
/* a number is not a valid datePart either */
|
|
37
|
+
set @y = DatePart('2026-01-15', 5)
|
|
38
|
+
]%%
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Allowed:**
|
|
42
|
+
|
|
43
|
+
```ampscript
|
|
44
|
+
%%[
|
|
45
|
+
/* exact-case enum value */
|
|
46
|
+
set @x = DatePart('2026-01-15', 'Y')
|
|
47
|
+
|
|
48
|
+
/* case-insensitive match */
|
|
49
|
+
set @y = DatePart('2026-01-15', 'year')
|
|
50
|
+
|
|
51
|
+
/* variable argument — not statically checkable, skipped */
|
|
52
|
+
set @z = DatePart(@d, @part)
|
|
53
|
+
]%%
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## When to Disable
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
// eslint.config.js
|
|
60
|
+
rules: { 'sfmc/amp-arg-types': 'off' }
|
|
61
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-sfmc",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "ESLint plugin for Salesforce Marketing Cloud  AMPscript and Server-Side JavaScript (SSJS)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
"sfmc"
|
|
33
33
|
],
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"ampscript-data": "^2.0.
|
|
35
|
+
"ampscript-data": "^2.0.3",
|
|
36
36
|
"ampscript-parser": "^0.1.3",
|
|
37
|
-
"ssjs-data": "^0.
|
|
37
|
+
"ssjs-data": "^0.5.0"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"eslint": ">=9.0.0"
|
package/src/index.js
CHANGED
|
@@ -23,6 +23,7 @@ import ampNoLoopCounterAssign from './rules/amp/no-loop-counter-assign.js';
|
|
|
23
23
|
import ampNoInlineStatement from './rules/amp/no-inline-statement.js';
|
|
24
24
|
import ampRequireVariableDeclaration from './rules/amp/require-variable-declaration.js';
|
|
25
25
|
import ampFunctionArity from './rules/amp/function-arity.js';
|
|
26
|
+
import ampArgTypes from './rules/amp/arg-types.js';
|
|
26
27
|
import ampNoEmailExcludedFunction from './rules/amp/no-email-excluded-function.js';
|
|
27
28
|
import ampNoDeprecatedFunction from './rules/amp/no-deprecated-function.js';
|
|
28
29
|
import ampNamingConvention from './rules/amp/naming-convention.js';
|
|
@@ -82,6 +83,7 @@ const plugin = {
|
|
|
82
83
|
'amp-no-inline-statement': ampNoInlineStatement,
|
|
83
84
|
'amp-require-variable-declaration': ampRequireVariableDeclaration,
|
|
84
85
|
'amp-function-arity': ampFunctionArity,
|
|
86
|
+
'amp-arg-types': ampArgTypes,
|
|
85
87
|
'amp-no-email-excluded-function': ampNoEmailExcludedFunction,
|
|
86
88
|
'amp-no-deprecated-function': ampNoDeprecatedFunction,
|
|
87
89
|
'amp-naming-convention': ampNamingConvention,
|
|
@@ -160,6 +162,7 @@ const ampRecommendedRules = {
|
|
|
160
162
|
'sfmc/amp-no-loop-counter-assign': 'warn',
|
|
161
163
|
'sfmc/amp-no-inline-statement': 'warn',
|
|
162
164
|
'sfmc/amp-function-arity': 'error',
|
|
165
|
+
'sfmc/amp-arg-types': 'error',
|
|
163
166
|
'sfmc/amp-no-deprecated-function': 'warn',
|
|
164
167
|
'sfmc/amp-naming-convention': 'warn',
|
|
165
168
|
'sfmc/amp-no-empty-then': 'warn',
|
|
@@ -181,6 +184,7 @@ const ampStrictRules = {
|
|
|
181
184
|
'sfmc/amp-no-inline-statement': 'error',
|
|
182
185
|
'sfmc/amp-require-variable-declaration': 'warn',
|
|
183
186
|
'sfmc/amp-function-arity': 'error',
|
|
187
|
+
'sfmc/amp-arg-types': 'error',
|
|
184
188
|
'sfmc/amp-no-email-excluded-function': ['error', { context: 'email' }],
|
|
185
189
|
'sfmc/amp-no-deprecated-function': 'error',
|
|
186
190
|
'sfmc/amp-naming-convention': ['error', { format: 'camelCase' }],
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: arg-types
|
|
3
|
+
*
|
|
4
|
+
* Validates literal arguments passed to AMPscript functions against the
|
|
5
|
+
* constraints declared in the ampscript-data catalog. Currently it checks
|
|
6
|
+
* parameters that declare an `enum` of allowed values: a static literal
|
|
7
|
+
* argument (string, number, or boolean) must be one of those values
|
|
8
|
+
* (case-insensitive). Variables and expressions are skipped because their
|
|
9
|
+
* value cannot be determined statically.
|
|
10
|
+
*
|
|
11
|
+
* This rule is the AMPscript counterpart of `ssjs-arg-types` and may be
|
|
12
|
+
* expanded later to cover additional argument-type checks.
|
|
13
|
+
*
|
|
14
|
+
* Example: DatePart('2026-01-15', 'decade') — 'decade' is not a valid datePart.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { functionLookup } from 'ampscript-data';
|
|
18
|
+
|
|
19
|
+
// AMPscript AST node types that represent a static, statically-resolvable literal.
|
|
20
|
+
const STATIC_LITERAL_TYPES = new Set(['StringLiteral', 'NumberLiteral', 'BooleanLiteral']);
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Returns the comparable string value of a static AMPscript literal node
|
|
24
|
+
* (string, number, or boolean), or null when the argument is not a static
|
|
25
|
+
* literal (e.g. a variable or expression) and cannot be validated against an
|
|
26
|
+
* enum.
|
|
27
|
+
*
|
|
28
|
+
* @param {object} arg - AMPscript argument AST node.
|
|
29
|
+
* @returns {string | null} The literal value as a string, or null.
|
|
30
|
+
*/
|
|
31
|
+
function staticLiteralValue(arg) {
|
|
32
|
+
if (!arg || !STATIC_LITERAL_TYPES.has(arg.type)) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
return String(arg.value);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export default {
|
|
39
|
+
meta: {
|
|
40
|
+
type: 'suggestion',
|
|
41
|
+
docs: {
|
|
42
|
+
description:
|
|
43
|
+
'Check that literal arguments match the expected parameter types and allowed values for AMPscript functions',
|
|
44
|
+
recommended: true,
|
|
45
|
+
},
|
|
46
|
+
messages: {
|
|
47
|
+
invalidEnumValue:
|
|
48
|
+
"Argument '{{param}}' of '{{name}}' must be one of: {{allowed}}. Received '{{actual}}'.",
|
|
49
|
+
},
|
|
50
|
+
schema: [],
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
create(context) {
|
|
54
|
+
return {
|
|
55
|
+
FunctionCall(node) {
|
|
56
|
+
const entry = functionLookup.get(node.name.toLowerCase());
|
|
57
|
+
if (!entry || !Array.isArray(entry.params)) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
for (const [i, arg] of node.arguments.entries()) {
|
|
62
|
+
const param = entry.params[i];
|
|
63
|
+
if (!param || !Array.isArray(param.enum) || param.enum.length === 0) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
// Only validate static literals (string, number, boolean);
|
|
67
|
+
// variables/expressions cannot be resolved statically.
|
|
68
|
+
const actual = staticLiteralValue(arg);
|
|
69
|
+
if (actual === null) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
const isAllowed = param.enum.some(
|
|
73
|
+
(v) => String(v).toLowerCase() === actual.toLowerCase(),
|
|
74
|
+
);
|
|
75
|
+
if (!isAllowed) {
|
|
76
|
+
context.report({
|
|
77
|
+
node: arg,
|
|
78
|
+
messageId: 'invalidEnumValue',
|
|
79
|
+
data: {
|
|
80
|
+
name: entry.name,
|
|
81
|
+
param: param.name,
|
|
82
|
+
allowed: param.enum.join(', '),
|
|
83
|
+
actual,
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
},
|
|
91
|
+
};
|