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.
Files changed (55) hide show
  1. package/README.md +43 -47
  2. package/docs/rules/ssjs/arg-types.md +72 -0
  3. package/docs/rules/ssjs/core-method-arity.md +72 -0
  4. package/docs/rules/ssjs/no-deprecated-function.md +56 -0
  5. package/docs/rules/ssjs/no-property-call.md +95 -0
  6. package/docs/rules/ssjs/no-unknown-function.md +57 -0
  7. package/package.json +18 -5
  8. package/src/ampscript-parser.js +11 -4
  9. package/src/ampscript-processor.js +9 -3
  10. package/src/index.js +23 -33
  11. package/src/processor.js +12 -4
  12. package/src/rules/amp/function-arity.js +3 -1
  13. package/src/rules/amp/naming-convention.js +7 -3
  14. package/src/rules/amp/no-deprecated-function.js +6 -2
  15. package/src/rules/amp/no-email-excluded-function.js +3 -1
  16. package/src/rules/amp/no-inline-statement.js +3 -1
  17. package/src/rules/amp/no-nested-ampscript-delimiter.js +5 -5
  18. package/src/rules/amp/no-nested-script-tag.js +3 -1
  19. package/src/rules/amp/no-smart-quotes.js +1 -1
  20. package/src/rules/amp/prefer-attribute-value.js +10 -4
  21. package/src/rules/amp/require-rowcount-check.js +3 -1
  22. package/src/rules/amp/require-variable-declaration.js +7 -3
  23. package/src/rules/ssjs/cache-loop-length.js +10 -4
  24. package/src/rules/ssjs/no-deprecated-function.js +181 -0
  25. package/src/rules/ssjs/no-hardcoded-credentials.js +3 -1
  26. package/src/rules/ssjs/no-property-call.js +136 -0
  27. package/src/rules/ssjs/no-treatascontent-injection.js +8 -4
  28. package/src/rules/ssjs/no-unavailable-method.js +30 -10
  29. package/src/rules/ssjs/no-unknown-function.js +236 -0
  30. package/src/rules/ssjs/no-unsupported-syntax.js +10 -5
  31. package/src/rules/ssjs/platform-function-arity.js +6 -2
  32. package/src/rules/ssjs/prefer-parsejson-safe-arg.js +17 -9
  33. package/src/rules/ssjs/prefer-platform-load-version.js +1 -4
  34. package/src/rules/ssjs/require-hasownproperty.js +34 -27
  35. package/src/rules/ssjs/require-platform-load-order.js +6 -2
  36. package/src/rules/ssjs/require-platform-load.js +32 -12
  37. package/src/rules/ssjs/ssjs-arg-types.js +345 -0
  38. package/src/rules/ssjs/ssjs-core-method-arity.js +176 -0
  39. package/src/ssjs-processor.js +3 -1
  40. package/docs/rules/ssjs/no-unknown-core-method.md +0 -46
  41. package/docs/rules/ssjs/no-unknown-http-method.md +0 -44
  42. package/docs/rules/ssjs/no-unknown-platform-client-browser.md +0 -44
  43. package/docs/rules/ssjs/no-unknown-platform-function.md +0 -44
  44. package/docs/rules/ssjs/no-unknown-platform-request.md +0 -43
  45. package/docs/rules/ssjs/no-unknown-platform-response.md +0 -43
  46. package/docs/rules/ssjs/no-unknown-platform-variable.md +0 -43
  47. package/docs/rules/ssjs/no-unknown-wsproxy-method.md +0 -46
  48. package/src/rules/ssjs/no-unknown-core-method.js +0 -103
  49. package/src/rules/ssjs/no-unknown-http-method.js +0 -41
  50. package/src/rules/ssjs/no-unknown-platform-client-browser.js +0 -50
  51. package/src/rules/ssjs/no-unknown-platform-function.js +0 -49
  52. package/src/rules/ssjs/no-unknown-platform-request.js +0 -50
  53. package/src/rules/ssjs/no-unknown-platform-response.js +0 -50
  54. package/src/rules/ssjs/no-unknown-platform-variable.js +0 -50
  55. 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
+ }
@@ -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) break;
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
- };