eslint-plugin-sfmc 0.1.3 → 1.0.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 +43 -47
- package/docs/rules/ssjs/arg-types.md +72 -0
- package/docs/rules/ssjs/core-method-arity.md +72 -0
- package/docs/rules/ssjs/no-deprecated-function.md +56 -0
- package/docs/rules/ssjs/no-property-call.md +95 -0
- package/docs/rules/ssjs/no-unknown-function.md +57 -0
- package/package.json +18 -5
- package/src/ampscript-parser.js +11 -4
- package/src/ampscript-processor.js +9 -3
- package/src/index.js +23 -33
- package/src/processor.js +12 -4
- package/src/rules/amp/function-arity.js +3 -1
- package/src/rules/amp/naming-convention.js +7 -3
- package/src/rules/amp/no-deprecated-function.js +6 -2
- package/src/rules/amp/no-email-excluded-function.js +3 -1
- package/src/rules/amp/no-inline-statement.js +3 -1
- package/src/rules/amp/no-nested-ampscript-delimiter.js +5 -5
- package/src/rules/amp/no-nested-script-tag.js +3 -1
- package/src/rules/amp/no-smart-quotes.js +1 -1
- package/src/rules/amp/prefer-attribute-value.js +10 -4
- package/src/rules/amp/require-rowcount-check.js +3 -1
- package/src/rules/amp/require-variable-declaration.js +7 -3
- package/src/rules/ssjs/cache-loop-length.js +10 -4
- package/src/rules/ssjs/no-deprecated-function.js +181 -0
- package/src/rules/ssjs/no-hardcoded-credentials.js +3 -1
- package/src/rules/ssjs/no-property-call.js +136 -0
- package/src/rules/ssjs/no-treatascontent-injection.js +8 -4
- package/src/rules/ssjs/no-unavailable-method.js +30 -10
- package/src/rules/ssjs/no-unknown-function.js +236 -0
- package/src/rules/ssjs/no-unsupported-syntax.js +10 -5
- package/src/rules/ssjs/platform-function-arity.js +6 -2
- package/src/rules/ssjs/prefer-parsejson-safe-arg.js +17 -9
- package/src/rules/ssjs/prefer-platform-load-version.js +1 -4
- package/src/rules/ssjs/require-hasownproperty.js +34 -27
- package/src/rules/ssjs/require-platform-load-order.js +6 -2
- package/src/rules/ssjs/require-platform-load.js +32 -12
- package/src/rules/ssjs/ssjs-arg-types.js +345 -0
- package/src/rules/ssjs/ssjs-core-method-arity.js +176 -0
- package/src/ssjs-processor.js +3 -1
- package/docs/rules/ssjs/no-unknown-core-method.md +0 -46
- package/docs/rules/ssjs/no-unknown-http-method.md +0 -44
- package/docs/rules/ssjs/no-unknown-platform-client-browser.md +0 -44
- package/docs/rules/ssjs/no-unknown-platform-function.md +0 -44
- package/docs/rules/ssjs/no-unknown-platform-request.md +0 -43
- package/docs/rules/ssjs/no-unknown-platform-response.md +0 -43
- package/docs/rules/ssjs/no-unknown-platform-variable.md +0 -43
- package/docs/rules/ssjs/no-unknown-wsproxy-method.md +0 -46
- package/src/rules/ssjs/no-unknown-core-method.js +0 -103
- package/src/rules/ssjs/no-unknown-http-method.js +0 -41
- package/src/rules/ssjs/no-unknown-platform-client-browser.js +0 -50
- package/src/rules/ssjs/no-unknown-platform-function.js +0 -49
- package/src/rules/ssjs/no-unknown-platform-request.js +0 -50
- package/src/rules/ssjs/no-unknown-platform-response.js +0 -50
- package/src/rules/ssjs/no-unknown-platform-variable.js +0 -50
- package/src/rules/ssjs/no-unknown-wsproxy-method.js +0 -71
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: ssjs-core-method-arity
|
|
3
|
+
*
|
|
4
|
+
* Enforces correct argument counts for Core Library object method calls.
|
|
5
|
+
* Covers both instance methods and static calls:
|
|
6
|
+
*
|
|
7
|
+
* var de = DataExtension.Init("MyDE");
|
|
8
|
+
* de.Add(rowObj); // instance
|
|
9
|
+
* BounceEvent.Retrieve(filter); // static single-name
|
|
10
|
+
* DataExtension.Rows.Add(rowObj); // static multi-part
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { coreObjectNames, coreMethodArityLookup } from 'ssjs-data';
|
|
14
|
+
|
|
15
|
+
export default {
|
|
16
|
+
meta: {
|
|
17
|
+
type: 'problem',
|
|
18
|
+
docs: {
|
|
19
|
+
description: 'Enforce correct argument counts for Core Library object methods',
|
|
20
|
+
},
|
|
21
|
+
messages: {
|
|
22
|
+
tooFewArgs:
|
|
23
|
+
"'{{call}}' requires at least {{min}} argument(s) but was called with {{actual}}.",
|
|
24
|
+
tooManyArgs:
|
|
25
|
+
"'{{call}}' accepts at most {{max}} argument(s) but was called with {{actual}}.",
|
|
26
|
+
},
|
|
27
|
+
schema: [],
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
create(context) {
|
|
31
|
+
const coreVars = new Map(); // varName → className
|
|
32
|
+
|
|
33
|
+
function checkArity(entry, args, callName, reportNode) {
|
|
34
|
+
if (!entry) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const actual = args.length;
|
|
38
|
+
if (actual < entry.minArgs) {
|
|
39
|
+
context.report({
|
|
40
|
+
node: reportNode,
|
|
41
|
+
messageId: 'tooFewArgs',
|
|
42
|
+
data: { call: callName, min: String(entry.minArgs), actual: String(actual) },
|
|
43
|
+
});
|
|
44
|
+
} else if (actual > entry.maxArgs) {
|
|
45
|
+
context.report({
|
|
46
|
+
node: reportNode,
|
|
47
|
+
messageId: 'tooManyArgs',
|
|
48
|
+
data: { call: callName, max: String(entry.maxArgs), actual: String(actual) },
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
VariableDeclaration(node) {
|
|
55
|
+
for (const decl of node.declarations) {
|
|
56
|
+
if (!decl.init || !decl.id || decl.id.type !== 'Identifier') {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
const coreType = getCoreInitType(decl.init);
|
|
60
|
+
if (coreType) {
|
|
61
|
+
coreVars.set(decl.id.name, coreType);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
AssignmentExpression(node) {
|
|
67
|
+
if (node.left.type !== 'Identifier') {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const coreType = getCoreInitType(node.right);
|
|
71
|
+
if (coreType) {
|
|
72
|
+
coreVars.set(node.left.name, coreType);
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
CallExpression(node) {
|
|
77
|
+
const callee = node.callee;
|
|
78
|
+
if (callee.type !== 'MemberExpression') {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (callee.property.type !== 'Identifier') {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const methodName = callee.property.name;
|
|
85
|
+
|
|
86
|
+
if (callee.object.type === 'Identifier') {
|
|
87
|
+
const objName = callee.object.name;
|
|
88
|
+
|
|
89
|
+
// Core Library instance method: de.Add(...)
|
|
90
|
+
const coreType = coreVars.get(objName);
|
|
91
|
+
if (coreType) {
|
|
92
|
+
const classLookup = coreMethodArityLookup.get(coreType.toLowerCase());
|
|
93
|
+
if (classLookup) {
|
|
94
|
+
const entry = classLookup.get(methodName.toLowerCase());
|
|
95
|
+
checkArity(
|
|
96
|
+
entry,
|
|
97
|
+
node.arguments,
|
|
98
|
+
`${coreType}.${methodName}`,
|
|
99
|
+
callee.property,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Static single-name: DataExtension.Init(...), BounceEvent.Retrieve(...)
|
|
106
|
+
if (coreObjectNames.has(objName)) {
|
|
107
|
+
const classLookup = coreMethodArityLookup.get(objName.toLowerCase());
|
|
108
|
+
if (classLookup) {
|
|
109
|
+
const entry = classLookup.get(methodName.toLowerCase());
|
|
110
|
+
checkArity(
|
|
111
|
+
entry,
|
|
112
|
+
node.arguments,
|
|
113
|
+
`${objName}.${methodName}`,
|
|
114
|
+
callee.property,
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Static multi-part: DataExtension.Rows.Add(...), TriggeredSend.Tracking.Clicks.Retrieve(...)
|
|
122
|
+
const objectPath = getMemberPath(callee.object);
|
|
123
|
+
if (objectPath && coreObjectNames.has(objectPath)) {
|
|
124
|
+
const classLookup = coreMethodArityLookup.get(objectPath.toLowerCase());
|
|
125
|
+
if (classLookup) {
|
|
126
|
+
const entry = classLookup.get(methodName.toLowerCase());
|
|
127
|
+
checkArity(
|
|
128
|
+
entry,
|
|
129
|
+
node.arguments,
|
|
130
|
+
`${objectPath}.${methodName}`,
|
|
131
|
+
callee.property,
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
function getCoreInitType(node) {
|
|
141
|
+
if (!node || node.type !== 'CallExpression') {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
const callee = node.callee;
|
|
145
|
+
if (callee.type !== 'MemberExpression') {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
if (callee.property.type !== 'Identifier' || callee.property.name !== 'Init') {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
if (callee.object.type === 'Identifier' && coreObjectNames.has(callee.object.name)) {
|
|
152
|
+
return callee.object.name;
|
|
153
|
+
}
|
|
154
|
+
if (
|
|
155
|
+
callee.object.type === 'MemberExpression' &&
|
|
156
|
+
callee.object.object.type === 'Identifier' &&
|
|
157
|
+
callee.object.property.type === 'Identifier'
|
|
158
|
+
) {
|
|
159
|
+
const fullName = `${callee.object.object.name}.${callee.object.property.name}`;
|
|
160
|
+
if (coreObjectNames.has(fullName)) {
|
|
161
|
+
return fullName;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function getMemberPath(node) {
|
|
168
|
+
if (node.type === 'Identifier') {
|
|
169
|
+
return node.name;
|
|
170
|
+
}
|
|
171
|
+
if (node.type === 'MemberExpression' && node.property.type === 'Identifier') {
|
|
172
|
+
const obj = getMemberPath(node.object);
|
|
173
|
+
return obj ? `${obj}.${node.property.name}` : null;
|
|
174
|
+
}
|
|
175
|
+
return null;
|
|
176
|
+
}
|
package/src/ssjs-processor.js
CHANGED
|
@@ -20,7 +20,9 @@ export function preprocess(text, filename) {
|
|
|
20
20
|
|
|
21
21
|
SCRIPT_CLOSE_RE.lastIndex = openEnd;
|
|
22
22
|
const closeMatch = SCRIPT_CLOSE_RE.exec(text);
|
|
23
|
-
if (!closeMatch)
|
|
23
|
+
if (!closeMatch) {
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
24
26
|
|
|
25
27
|
const jsCode = text.slice(openEnd, closeMatch.index);
|
|
26
28
|
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
# `sfmc/ssjs-no-unknown-core-method`
|
|
2
|
-
|
|
3
|
-
> Disallow calls to methods that don't exist on a Core library object.
|
|
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 a variable is initialised with a known Core library constructor (e.g. `var de = DataExtension.Init("MyDE")`), only the methods documented for that Core object type should be called on it. Calling an undocumented or misspelled method causes a runtime error. This rule infers the object type from the `.Init()` call and validates subsequent method calls.
|
|
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
|
-
```js
|
|
28
|
-
Platform.Load("core", "1.1.5");
|
|
29
|
-
var de = DataExtension.Init("MyDE");
|
|
30
|
-
de.Execute(); /* 'Execute' is not a method of DataExtension */
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
**Allowed:**
|
|
34
|
-
|
|
35
|
-
```js
|
|
36
|
-
Platform.Load("core", "1.1.5");
|
|
37
|
-
var de = DataExtension.Init("MyDE");
|
|
38
|
-
var rows = de.Rows.Retrieve();
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
## When to Disable
|
|
42
|
-
|
|
43
|
-
```js
|
|
44
|
-
// eslint.config.js
|
|
45
|
-
rules: { 'sfmc/ssjs-no-unknown-core-method': 'off' }
|
|
46
|
-
```
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
# `sfmc/ssjs-no-unknown-http-method`
|
|
2
|
-
|
|
3
|
-
> Disallow unknown `HTTP.*` method calls.
|
|
4
|
-
|
|
5
|
-
| | |
|
|
6
|
-
|---|---|
|
|
7
|
-
| **Type** | `problem` |
|
|
8
|
-
| **Default severity** | `error` in `recommended` and `strict` |
|
|
9
|
-
| **Fixable** | — |
|
|
10
|
-
|
|
11
|
-
## Why This Rule Exists
|
|
12
|
-
|
|
13
|
-
SFMC's SSJS runtime exposes only four `HTTP` methods: `HTTP.Get`, `HTTP.Post`, `HTTP.GetRequest`, and `HTTP.PostRequest`. Calling any other `HTTP.*` method causes a runtime error. This rule validates every `HTTP.*` call against that fixed list.
|
|
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
|
-
```js
|
|
28
|
-
var response = HTTP.Delete("https://api.example.com/resource/1");
|
|
29
|
-
var response = HTTP.Patch("https://api.example.com/resource/1", payload);
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
**Allowed:**
|
|
33
|
-
|
|
34
|
-
```js
|
|
35
|
-
var getResponse = HTTP.Get("https://api.example.com/resource/1");
|
|
36
|
-
var postResponse = HTTP.Post("https://api.example.com/resource", payload, contentType);
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
## When to Disable
|
|
40
|
-
|
|
41
|
-
```js
|
|
42
|
-
// eslint.config.js
|
|
43
|
-
rules: { 'sfmc/ssjs-no-unknown-http-method': 'off' }
|
|
44
|
-
```
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
# `sfmc/ssjs-no-unknown-platform-client-browser`
|
|
2
|
-
|
|
3
|
-
> Disallow unknown `Platform.ClientBrowser.*` method calls.
|
|
4
|
-
|
|
5
|
-
| | |
|
|
6
|
-
|---|---|
|
|
7
|
-
| **Type** | `problem` |
|
|
8
|
-
| **Default severity** | `error` in `recommended` and `strict` |
|
|
9
|
-
| **Fixable** | — |
|
|
10
|
-
|
|
11
|
-
## Why This Rule Exists
|
|
12
|
-
|
|
13
|
-
`Platform.ClientBrowser` exposes a small, fixed set of methods for writing to and redirecting the HTTP response on CloudPages and landing pages. Calling a method that does not exist in this catalog causes a runtime error. This rule validates every `Platform.ClientBrowser.*` call against the known list.
|
|
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
|
-
```js
|
|
28
|
-
Platform.ClientBrowser.Send("data");
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
**Allowed:**
|
|
32
|
-
|
|
33
|
-
```js
|
|
34
|
-
Platform.ClientBrowser.Redirect("https://example.com");
|
|
35
|
-
Platform.ClientBrowser.Write("<p>Hello</p>");
|
|
36
|
-
Platform.ClientBrowser.SetCookie("session", "abc123");
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
## When to Disable
|
|
40
|
-
|
|
41
|
-
```js
|
|
42
|
-
// eslint.config.js
|
|
43
|
-
rules: { 'sfmc/ssjs-no-unknown-platform-client-browser': 'off' }
|
|
44
|
-
```
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
# `sfmc/ssjs-no-unknown-platform-function`
|
|
2
|
-
|
|
3
|
-
> Disallow calls to unknown `Platform.Function.*` methods.
|
|
4
|
-
|
|
5
|
-
| | |
|
|
6
|
-
|---|---|
|
|
7
|
-
| **Type** | `problem` |
|
|
8
|
-
| **Default severity** | `error` in `recommended` and `strict` |
|
|
9
|
-
| **Fixable** | — |
|
|
10
|
-
|
|
11
|
-
## Why This Rule Exists
|
|
12
|
-
|
|
13
|
-
`Platform.Function` exposes a fixed set of SFMC server-side utility functions (e.g. `Platform.Function.Lookup`, `Platform.Function.InsertData`). Calling a method that does not exist in the catalog causes a runtime error. This rule validates every `Platform.Function.*` call against the known list.
|
|
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
|
-
```js
|
|
28
|
-
Platform.Load("core", "1.1.5");
|
|
29
|
-
var result = Platform.Function.CustomQuery("MyDE");
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
**Allowed:**
|
|
33
|
-
|
|
34
|
-
```js
|
|
35
|
-
Platform.Load("core", "1.1.5");
|
|
36
|
-
var result = Platform.Function.Lookup("MyDE", "Value", "Key", keyValue);
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
## When to Disable
|
|
40
|
-
|
|
41
|
-
```js
|
|
42
|
-
// eslint.config.js
|
|
43
|
-
rules: { 'sfmc/ssjs-no-unknown-platform-function': 'off' }
|
|
44
|
-
```
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
# `sfmc/ssjs-no-unknown-platform-request`
|
|
2
|
-
|
|
3
|
-
> Disallow unknown `Platform.Request.*` method calls.
|
|
4
|
-
|
|
5
|
-
| | |
|
|
6
|
-
|---|---|
|
|
7
|
-
| **Type** | `problem` |
|
|
8
|
-
| **Default severity** | `error` in `recommended` and `strict` |
|
|
9
|
-
| **Fixable** | — |
|
|
10
|
-
|
|
11
|
-
## Why This Rule Exists
|
|
12
|
-
|
|
13
|
-
`Platform.Request` exposes a fixed set of methods for reading incoming HTTP request data on CloudPages and landing pages (e.g. `Platform.Request.GetQueryStringParameter`, `Platform.Request.GetFormField`). Calling an unknown method causes a runtime error. This rule validates every `Platform.Request.*` call against the known list.
|
|
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
|
-
```js
|
|
28
|
-
var val = Platform.Request.ReadParam("myParam");
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
**Allowed:**
|
|
32
|
-
|
|
33
|
-
```js
|
|
34
|
-
var val = Platform.Request.GetQueryStringParameter("myParam");
|
|
35
|
-
var field = Platform.Request.GetFormField("email");
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## When to Disable
|
|
39
|
-
|
|
40
|
-
```js
|
|
41
|
-
// eslint.config.js
|
|
42
|
-
rules: { 'sfmc/ssjs-no-unknown-platform-request': 'off' }
|
|
43
|
-
```
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
# `sfmc/ssjs-no-unknown-platform-response`
|
|
2
|
-
|
|
3
|
-
> Disallow unknown `Platform.Response.*` method calls.
|
|
4
|
-
|
|
5
|
-
| | |
|
|
6
|
-
|---|---|
|
|
7
|
-
| **Type** | `problem` |
|
|
8
|
-
| **Default severity** | `error` in `recommended` and `strict` |
|
|
9
|
-
| **Fixable** | — |
|
|
10
|
-
|
|
11
|
-
## Why This Rule Exists
|
|
12
|
-
|
|
13
|
-
`Platform.Response` exposes a fixed set of methods for controlling HTTP responses from CloudPages and landing pages (e.g. `Platform.Response.Write`, `Platform.Response.Redirect`). Calling an unknown method causes a runtime error. This rule validates every `Platform.Response.*` call against the known list.
|
|
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
|
-
```js
|
|
28
|
-
Platform.Response.Send("text/html", "<p>Hello</p>");
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
**Allowed:**
|
|
32
|
-
|
|
33
|
-
```js
|
|
34
|
-
Platform.Response.Write("<p>Hello</p>");
|
|
35
|
-
Platform.Response.Redirect("https://example.com");
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## When to Disable
|
|
39
|
-
|
|
40
|
-
```js
|
|
41
|
-
// eslint.config.js
|
|
42
|
-
rules: { 'sfmc/ssjs-no-unknown-platform-response': 'off' }
|
|
43
|
-
```
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
# `sfmc/ssjs-no-unknown-platform-variable`
|
|
2
|
-
|
|
3
|
-
> Disallow unknown `Platform.Variable.*` method calls.
|
|
4
|
-
|
|
5
|
-
| | |
|
|
6
|
-
|---|---|
|
|
7
|
-
| **Type** | `problem` |
|
|
8
|
-
| **Default severity** | `error` in `recommended` and `strict` |
|
|
9
|
-
| **Fixable** | — |
|
|
10
|
-
|
|
11
|
-
## Why This Rule Exists
|
|
12
|
-
|
|
13
|
-
`Platform.Variable` exposes a fixed set of methods for interacting with AMPscript variables from SSJS (e.g. `Platform.Variable.GetValue`, `Platform.Variable.SetValue`). Calling an unknown method causes a runtime error. This rule validates every `Platform.Variable.*` call against the known list.
|
|
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
|
-
```js
|
|
28
|
-
var val = Platform.Variable.ReadValue("@myVar");
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
**Allowed:**
|
|
32
|
-
|
|
33
|
-
```js
|
|
34
|
-
var val = Platform.Variable.GetValue("@myVar");
|
|
35
|
-
Platform.Variable.SetValue("@myVar", "Hello");
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## When to Disable
|
|
39
|
-
|
|
40
|
-
```js
|
|
41
|
-
// eslint.config.js
|
|
42
|
-
rules: { 'sfmc/ssjs-no-unknown-platform-variable': 'off' }
|
|
43
|
-
```
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
# `sfmc/ssjs-no-unknown-wsproxy-method`
|
|
2
|
-
|
|
3
|
-
> Disallow unknown method calls on a `WSProxy` instance.
|
|
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
|
-
`WSProxy` is SFMC's SSJS wrapper around the SOAP API. It exposes a specific set of methods (`retrieve`, `retrieveAll`, `create`, `update`, `delete`, `execute`, `perform`). Calling an unknown method on a `WSProxy` instance causes a runtime error. This rule infers `WSProxy` variable usage and validates method calls against the known list.
|
|
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
|
-
```js
|
|
28
|
-
Platform.Load("core", "1.1.5");
|
|
29
|
-
var proxy = new Script.Util.WSProxy();
|
|
30
|
-
var result = proxy.query({ ObjectType: "Subscriber" });
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
**Allowed:**
|
|
34
|
-
|
|
35
|
-
```js
|
|
36
|
-
Platform.Load("core", "1.1.5");
|
|
37
|
-
var proxy = new Script.Util.WSProxy();
|
|
38
|
-
var result = proxy.retrieve({ ObjectType: "Subscriber", Properties: ["SubscriberKey"] }, []);
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
## When to Disable
|
|
42
|
-
|
|
43
|
-
```js
|
|
44
|
-
// eslint.config.js
|
|
45
|
-
rules: { 'sfmc/ssjs-no-unknown-wsproxy-method': 'off' }
|
|
46
|
-
```
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Rule: no-unknown-core-method
|
|
3
|
-
*
|
|
4
|
-
* Flags calls to Core library object methods that don't exist on that
|
|
5
|
-
* object type, when the object type can be inferred from an Init call.
|
|
6
|
-
*
|
|
7
|
-
* Example:
|
|
8
|
-
* var de = DataExtension.Init("MyDE");
|
|
9
|
-
* de.Foo(); // error: 'Foo' is not a method of DataExtension
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { coreObjectLookup, coreObjectNames } from 'ssjs-data';
|
|
13
|
-
|
|
14
|
-
export default {
|
|
15
|
-
meta: {
|
|
16
|
-
type: 'problem',
|
|
17
|
-
docs: {
|
|
18
|
-
description: "Disallow calls to methods that don't exist on a Core library object",
|
|
19
|
-
},
|
|
20
|
-
messages: {
|
|
21
|
-
unknownMethod:
|
|
22
|
-
"'{{method}}' is not a known method of {{objectType}}. Available methods: {{available}}.",
|
|
23
|
-
},
|
|
24
|
-
schema: [],
|
|
25
|
-
},
|
|
26
|
-
|
|
27
|
-
create(context) {
|
|
28
|
-
const variableTypes = new Map();
|
|
29
|
-
|
|
30
|
-
return {
|
|
31
|
-
VariableDeclaration(node) {
|
|
32
|
-
for (const decl of node.declarations) {
|
|
33
|
-
if (!decl.init || !decl.id || decl.id.type !== 'Identifier') continue;
|
|
34
|
-
const coreType = getCoreInitType(decl.init);
|
|
35
|
-
if (coreType) {
|
|
36
|
-
variableTypes.set(decl.id.name, coreType);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
},
|
|
40
|
-
|
|
41
|
-
AssignmentExpression(node) {
|
|
42
|
-
if (node.left.type !== 'Identifier') return;
|
|
43
|
-
const coreType = getCoreInitType(node.right);
|
|
44
|
-
if (coreType) {
|
|
45
|
-
variableTypes.set(node.left.name, coreType);
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
|
|
49
|
-
CallExpression(node) {
|
|
50
|
-
const callee = node.callee;
|
|
51
|
-
if (callee.type !== 'MemberExpression') return;
|
|
52
|
-
if (callee.object.type !== 'Identifier') return;
|
|
53
|
-
if (callee.property.type !== 'Identifier') return;
|
|
54
|
-
|
|
55
|
-
const objectName = callee.object.name;
|
|
56
|
-
const method = callee.property.name;
|
|
57
|
-
|
|
58
|
-
const coreType = variableTypes.get(objectName);
|
|
59
|
-
if (!coreType) return;
|
|
60
|
-
|
|
61
|
-
const objectDef = coreObjectLookup.get(coreType);
|
|
62
|
-
if (!objectDef) return;
|
|
63
|
-
|
|
64
|
-
const knownMethods = new Set(objectDef.methods.map((m) => m.toLowerCase()));
|
|
65
|
-
if (!knownMethods.has(method.toLowerCase())) {
|
|
66
|
-
context.report({
|
|
67
|
-
node: callee.property,
|
|
68
|
-
messageId: 'unknownMethod',
|
|
69
|
-
data: {
|
|
70
|
-
method,
|
|
71
|
-
objectType: coreType,
|
|
72
|
-
available: objectDef.methods.join(', '),
|
|
73
|
-
},
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
},
|
|
77
|
-
};
|
|
78
|
-
},
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
function getCoreInitType(node) {
|
|
82
|
-
if (!node || node.type !== 'CallExpression') return null;
|
|
83
|
-
const callee = node.callee;
|
|
84
|
-
if (callee.type !== 'MemberExpression') return null;
|
|
85
|
-
|
|
86
|
-
if (callee.property.type === 'Identifier' && callee.property.name === 'Init') {
|
|
87
|
-
if (callee.object.type === 'Identifier' && coreObjectNames.has(callee.object.name)) {
|
|
88
|
-
return callee.object.name;
|
|
89
|
-
}
|
|
90
|
-
if (
|
|
91
|
-
callee.object.type === 'MemberExpression' &&
|
|
92
|
-
callee.object.object.type === 'Identifier' &&
|
|
93
|
-
callee.object.property.type === 'Identifier'
|
|
94
|
-
) {
|
|
95
|
-
const fullName = `${callee.object.object.name}.${callee.object.property.name}`;
|
|
96
|
-
if (coreObjectNames.has(fullName)) {
|
|
97
|
-
return fullName;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return null;
|
|
103
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Rule: no-unknown-http-method
|
|
3
|
-
*
|
|
4
|
-
* Flags calls to HTTP.* where the method name does not exist
|
|
5
|
-
* in the known SFMC HTTP method catalog.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { httpMethodNames } from 'ssjs-data';
|
|
9
|
-
|
|
10
|
-
export default {
|
|
11
|
-
meta: {
|
|
12
|
-
type: 'problem',
|
|
13
|
-
docs: {
|
|
14
|
-
description: 'Disallow calls to unknown HTTP object methods',
|
|
15
|
-
},
|
|
16
|
-
messages: {
|
|
17
|
-
unknownMethod: "'HTTP.{{name}}' is not a recognized SFMC HTTP method.",
|
|
18
|
-
},
|
|
19
|
-
schema: [],
|
|
20
|
-
},
|
|
21
|
-
|
|
22
|
-
create(context) {
|
|
23
|
-
return {
|
|
24
|
-
CallExpression(node) {
|
|
25
|
-
const callee = node.callee;
|
|
26
|
-
if (callee.type !== 'MemberExpression') return;
|
|
27
|
-
if (callee.object.type !== 'Identifier' || callee.object.name !== 'HTTP') return;
|
|
28
|
-
if (callee.property.type !== 'Identifier') return;
|
|
29
|
-
|
|
30
|
-
const methodName = callee.property.name;
|
|
31
|
-
if (!httpMethodNames.has(methodName.toLowerCase())) {
|
|
32
|
-
context.report({
|
|
33
|
-
node: callee.property,
|
|
34
|
-
messageId: 'unknownMethod',
|
|
35
|
-
data: { name: methodName },
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
},
|
|
39
|
-
};
|
|
40
|
-
},
|
|
41
|
-
};
|