eslint-plugin-sfmc 1.0.1 → 2.0.1

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 CHANGED
@@ -25,8 +25,41 @@ export default [
25
25
  ];
26
26
  ```
27
27
 
28
+ ## VS Code Setup
29
+
30
+ To see `eslint(sfmc/...)` diagnostics in VS Code for `.amp`, `.ssjs`, and `.html` files you need the [VS Code ESLint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) to validate the custom SFMC language IDs.
31
+
32
+ **Option A — Install `vscode-sfmc-language`** (recommended)
33
+
34
+ The [SFMC Language Service extension](https://marketplace.visualstudio.com/items?itemName=joernberkefeld.sfmc-language) contributes the SFMC language IDs **and** automatically configures `eslint.validate` for you. No manual settings required.
35
+
36
+ **Option B — Manual configuration**
37
+
38
+ Add the following to your `.vscode/settings.json`:
39
+
40
+ ```json
41
+ {
42
+ "eslint.validate": [
43
+ "javascript",
44
+ "javascriptreact",
45
+ "typescript",
46
+ "typescriptreact",
47
+ "html",
48
+ "vue",
49
+ "markdown",
50
+ "ampscript",
51
+ "ssjs",
52
+ "sfmc"
53
+ ]
54
+ }
55
+ ```
56
+
57
+ > **Why `eslint.validate` and not `eslint.probe`?** `eslint.probe` silently skips files for language IDs that the ESLint extension does not natively recognise. `eslint.validate` forces the extension to process those files regardless of language ID.
58
+
28
59
  ## Configs
29
60
 
61
+ ### Marketing Cloud Engagement (default)
62
+
30
63
  | Config | Files | What it does |
31
64
  | -------------------------- | ---------------------------- | --------------------------------------------------------- |
32
65
  | `sfmc.configs.ampscript` | `**/*.ampscript`, `**/*.amp` | AMPscript rules only (recommended severity) |
@@ -37,6 +70,30 @@ export default [
37
70
 
38
71
  `recommended`, `embedded`, and `strict` are arrays — spread them with `...`.
39
72
 
73
+ ### Marketing Cloud Next
74
+
75
+ Use the `-next` config variants when targeting **Marketing Cloud Next (MCN)**. MCN supports only a subset of AMPscript functions and does **not** support SSJS at all.
76
+
77
+ | Config | Files | What it does |
78
+ | -------------------------------- | ---------------------------- | ----------------------------------------------------------------------------------------- |
79
+ | `sfmc.configs['ampscript-next']` | `**/*.ampscript`, `**/*.amp` | AMPscript rules + flags functions unsupported in MCN (single config object) |
80
+ | `sfmc.configs['ssjs-next']` | `**/*.ssjs` | Flags all SSJS API calls as MCN-unsupported; all other SSJS quality rules disabled |
81
+ | `sfmc.configs['recommended-next']` | Both of the above | AMPscript MCN-aware + SSJS flagged for standalone files |
82
+ | `sfmc.configs['embedded-next']` | `**/*.html` | AMPscript MCN-aware + SSJS flagged for HTML-embedded code |
83
+ | `sfmc.configs['strict-next']` | All of the above + HTML | All AMPscript rules at `error` severity + MCN flag; SSJS fully flagged |
84
+
85
+ `recommended-next`, `embedded-next`, and `strict-next` are arrays — spread them with `...`.
86
+
87
+ ```js
88
+ // eslint.config.js — targeting Marketing Cloud Next
89
+ import sfmc from 'eslint-plugin-sfmc';
90
+
91
+ export default [
92
+ ...sfmc.configs['recommended-next'],
93
+ ...sfmc.configs['embedded-next'],
94
+ ];
95
+ ```
96
+
40
97
  ## AMPscript Rules (`amp-*`)
41
98
 
42
99
  | Rule | Default | Description |
@@ -5,22 +5,30 @@
5
5
  | | |
6
6
  |---|---|
7
7
  | **Type** | `problem` |
8
- | **Default severity** | `error` in `recommended` and `strict` |
8
+ | **Default severity** | `error` in `recommended`, `strict`, and all `-next` configs |
9
9
  | **Fixable** | — |
10
10
 
11
11
  ## Why This Rule Exists
12
12
 
13
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
14
 
15
+ When targeting **Marketing Cloud Next**, only a subset of AMPscript functions are supported. Use the `target: 'next'` option to flag functions that cannot run in MCN.
16
+
15
17
  ## Settings
16
18
 
17
19
  | Setting | Values | Default |
18
20
  |---------|--------|---------|
19
21
  | severity | `"error"` \| `"warn"` \| `"off"` | `"error"` |
20
22
 
21
- This rule has no configuration options.
23
+ ### Options
24
+
25
+ | Option | Type | Default | Description |
26
+ |--------|------|---------|-------------|
27
+ | `target` | `'engagement'` \| `'next'` | — | Target platform. Set to `'next'` to additionally flag AMPscript functions not available in Marketing Cloud Next (API v67.0+). |
28
+
29
+ ## Examples
22
30
 
23
- ### Examples
31
+ ### Standard usage (Marketing Cloud Engagement)
24
32
 
25
33
  **Not allowed:**
26
34
 
@@ -40,6 +48,37 @@ This rule has no configuration options.
40
48
  ]%%
41
49
  ```
42
50
 
51
+ ### MCN target
52
+
53
+ Configure the rule with `target: 'next'` to flag functions unsupported in Marketing Cloud Next:
54
+
55
+ ```js
56
+ // eslint.config.js
57
+ rules: {
58
+ 'sfmc/amp-no-unknown-function': ['error', { target: 'next' }]
59
+ }
60
+ ```
61
+
62
+ Or use the built-in `recommended-next` / `strict-next` / `embedded-next` configs which apply this automatically.
63
+
64
+ **Not allowed (with `target: 'next'`):**
65
+
66
+ ```ampscript
67
+ %%[
68
+ /* InsertDE is not supported in Marketing Cloud Next */
69
+ InsertDE("MyDE", "Col", "Value")
70
+ ]%%
71
+ ```
72
+
73
+ **Allowed (with `target: 'next'`):**
74
+
75
+ ```ampscript
76
+ %%[
77
+ /* UpsertDE is supported in Marketing Cloud Next */
78
+ UpsertDE("MyDE", 1, "Key", @key, "Col", "Value")
79
+ ]%%
80
+ ```
81
+
43
82
  ## When to Disable
44
83
 
45
84
  Only disable this rule if you are intentionally using a proprietary or undocumented SFMC extension that is not in the public catalog.
@@ -1,4 +1,4 @@
1
- # ssjs-no-deprecated-function
1
+ # `sfmc/ssjs-no-deprecated-function`
2
2
 
3
3
  Flags calls to deprecated SFMC SSJS APIs. Currently focuses on the **Content Areas** feature,
4
4
  which has been retired and no longer allows creating or updating content.
@@ -1,4 +1,4 @@
1
- # ssjs-no-property-call
1
+ # `sfmc/ssjs-no-property-call`
2
2
 
3
3
  Flags `Platform.Request` and `Platform.Response` members that are **properties**, not functions.
4
4
  Calling them with parentheses `()` is a runtime error — they must be accessed without parentheses.
@@ -1,4 +1,4 @@
1
- # ssjs-no-switch-default
1
+ # `sfmc/ssjs-no-switch-default`
2
2
 
3
3
  Disallow `default` case in `switch` statements.
4
4
 
@@ -1,4 +1,4 @@
1
- # ssjs-no-treatascontent-injection
1
+ # `sfmc/ssjs-no-treatascontent-injection`
2
2
 
3
3
  Disallow string concatenation inside `TreatAsContent()` calls.
4
4
 
@@ -1,7 +1,9 @@
1
- # ssjs-no-unknown-function
1
+ # `sfmc/ssjs-no-unknown-function`
2
2
 
3
3
  Flags calls to SFMC API methods that do not exist in the catalog. This single rule replaces the seven narrow `no-unknown-*` rules from earlier versions of the plugin.
4
4
 
5
+ When targeting **Marketing Cloud Next**, SSJS is not supported at all. Set `target: 'next'` to flag every SSJS API call as an error, signalling that the SSJS block must be removed or rewritten in AMPscript.
6
+
5
7
  ## Covered namespaces
6
8
 
7
9
  | Namespace | Example |
@@ -15,9 +17,17 @@ Flags calls to SFMC API methods that do not exist in the catalog. This single ru
15
17
  | Core Library instance methods | `de.Rows.Retrieve()` (tracked from `DataExtension.Init(…)`) |
16
18
  | WSProxy instance methods | `api.retrieve(…)` (tracked from `new Script.Util.WSProxy()`) |
17
19
 
20
+ ## Options
21
+
22
+ | Option | Type | Default | Description |
23
+ |--------|------|---------|-------------|
24
+ | `target` | `'engagement'` \| `'next'` | — | Target platform. Set to `'next'` to flag every SSJS API call (all of `Platform.*`, `HTTP.*`, Core Library, and WSProxy) as unsupported in Marketing Cloud Next. |
25
+
18
26
  ## Examples
19
27
 
20
- ### Incorrect
28
+ ### Standard usage (Marketing Cloud Engagement)
29
+
30
+ #### ❌ Incorrect
21
31
 
22
32
  ```js
23
33
  // Typo in Platform.Function method name
@@ -32,7 +42,7 @@ var de = DataExtension.Init('CustomerData');
32
42
  de.ExportRows();
33
43
  ```
34
44
 
35
- ### ✅ Correct
45
+ #### ✅ Correct
36
46
 
37
47
  ```js
38
48
  Platform.Function.LookupRows('MyDE', 'Status', 'Active');
@@ -44,14 +54,37 @@ var de = DataExtension.Init('CustomerData');
44
54
  de.Rows.Retrieve();
45
55
  ```
46
56
 
57
+ ### MCN target (`target: 'next'`)
58
+
59
+ SSJS is not supported in Marketing Cloud Next. Configuring `target: 'next'` flags every SFMC API call within SSJS files, indicating they must be rewritten or removed.
60
+
61
+ ```js
62
+ // eslint.config.js
63
+ rules: {
64
+ 'sfmc/ssjs-no-unknown-function': ['error', { target: 'next' }]
65
+ }
66
+ ```
67
+
68
+ Or use the built-in `recommended-next` / `strict-next` / `embedded-next` configs which apply this automatically (and disable all other SSJS quality rules, since the entire SSJS block must be migrated).
69
+
70
+ #### ❌ Flagged (with `target: 'next'`)
71
+
72
+ ```js
73
+ // All SFMC API calls are flagged — SSJS is not available in MCN
74
+ Platform.Function.LookupRows('MyDE', 'Status', 'Active');
75
+
76
+ var de = DataExtension.Init('CustomerData');
77
+ de.Rows.Retrieve();
78
+ ```
79
+
47
80
  ## When to use
48
81
 
49
- Enable this rule to catch typos, outdated method names, and unsupported API calls before
50
- they cause runtime failures.
82
+ Enable this rule to catch typos, outdated method names, and unsupported API calls before they cause runtime failures. Use `target: 'next'` when migrating content to Marketing Cloud Next.
51
83
 
52
84
  ## Rule details
53
85
 
54
86
  - **Type:** `problem`
55
87
  - **Fixable:** No
56
- - **Recommended:** Yes
57
- - **Strict:** Yes
88
+ - **Recommended:** Yes (`engagement` mode)
89
+ - **Strict:** Yes (`engagement` mode)
90
+ - **`recommended-next` / `strict-next` / `embedded-next`:** Yes (`next` mode — all other SSJS rules disabled)
@@ -1,4 +1,4 @@
1
- # ssjs-prefer-parsejson-safe-arg
1
+ # `sfmc/ssjs-prefer-parsejson-safe-arg`
2
2
 
3
3
  Require concatenating an empty string to `Platform.Function.ParseJSON()` arguments.
4
4
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-sfmc",
3
- "version": "1.0.1",
3
+ "version": "2.0.1",
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,15 +32,17 @@
32
32
  "sfmc"
33
33
  ],
34
34
  "dependencies": {
35
- "ampscript-data": "^0.1.4",
36
- "ampscript-parser": "^0.1.1",
37
- "ssjs-data": "^0.3.1"
35
+ "ampscript-data": "^1.0.0",
36
+ "ampscript-parser": "^0.1.3",
37
+ "ssjs-data": "^0.4.0"
38
38
  },
39
39
  "peerDependencies": {
40
40
  "eslint": ">=9.0.0"
41
41
  },
42
42
  "scripts": {
43
- "test": "node --test tests/rules.test.js",
43
+ "test": "node --test tests/rules.test.js && node --test tests/fixtures.test.mjs",
44
+ "test:fixtures": "node --test tests/fixtures.test.mjs",
45
+ "test:fixtures:update": "node --test tests/fixtures.test.mjs -- --update-snapshots",
44
46
  "lint": "eslint .",
45
47
  "lint:fix": "eslint --fix .",
46
48
  "prepare": "husky || true",
package/src/index.js CHANGED
@@ -121,6 +121,34 @@ const plugin = {
121
121
  configs: {},
122
122
  };
123
123
 
124
+ // ── MCN SSJS rule set (all SSJS rules off except ssjs-no-unknown-function) ────
125
+
126
+ /**
127
+ * SSJS rules for MCN targets.
128
+ * All quality rules are disabled — only the presence of SSJS is flagged,
129
+ * because SSJS as a whole must be deleted when targeting Marketing Cloud Next.
130
+ */
131
+ const ssjsMcnRules = {
132
+ 'sfmc/ssjs-no-unknown-function': ['error', { target: 'next' }],
133
+ 'sfmc/ssjs-require-platform-load': 'off',
134
+ 'sfmc/ssjs-no-unsupported-syntax': 'off',
135
+ 'sfmc/ssjs-no-deprecated-function': 'off',
136
+ 'sfmc/ssjs-no-property-call': 'off',
137
+ 'sfmc/ssjs-platform-function-arity': 'off',
138
+ 'sfmc/ssjs-cache-loop-length': 'off',
139
+ 'sfmc/ssjs-require-hasownproperty': 'off',
140
+ 'sfmc/ssjs-require-platform-load-order': 'off',
141
+ 'sfmc/ssjs-no-hardcoded-credentials': 'off',
142
+ 'sfmc/ssjs-prefer-platform-load-version': 'off',
143
+ 'sfmc/ssjs-no-unavailable-method': 'off',
144
+ 'sfmc/ssjs-prefer-parsejson-safe-arg': 'off',
145
+ 'sfmc/ssjs-no-switch-default': 'off',
146
+ 'sfmc/ssjs-no-treatascontent-injection': 'off',
147
+ 'sfmc/ssjs-arg-types': 'off',
148
+ 'sfmc/ssjs-core-method-arity': 'off',
149
+ 'no-cond-assign': 'off',
150
+ };
151
+
124
152
  // ── AMPscript rule sets ───────────────────────────────────────────────────────
125
153
 
126
154
  const ampRecommendedRules = {
@@ -340,6 +368,153 @@ Object.assign(plugin.configs, {
340
368
  rules: { ...ssjsStrictRules },
341
369
  },
342
370
  ],
371
+
372
+ // ── Marketing Cloud Next configs ──────────────────────────────────────────
373
+
374
+ /**
375
+ * AMPscript-only MCN config. Flags functions not supported in MCN.
376
+ * No SSJS portion (AMPscript-only config).
377
+ */
378
+ 'ampscript-next': {
379
+ name: 'sfmc/ampscript-next',
380
+ plugins: { sfmc: plugin },
381
+ languageOptions: { parser: ampscriptParser },
382
+ files: ['**/*.ampscript', '**/*.amp'],
383
+ rules: {
384
+ ...ampRecommendedRules,
385
+ 'sfmc/amp-no-unknown-function': ['error', { target: 'next' }],
386
+ },
387
+ },
388
+
389
+ /**
390
+ * SSJS-only MCN config. Flags all SSJS API calls; disables all other SSJS rules.
391
+ */
392
+ 'ssjs-next': {
393
+ name: 'sfmc/ssjs-next',
394
+ plugins: { sfmc: plugin },
395
+ files: ['**/*.ssjs'],
396
+ languageOptions: {
397
+ ecmaVersion: 5,
398
+ sourceType: 'script',
399
+ globals: SSJS_GLOBALS_MAP,
400
+ },
401
+ rules: { ...ssjsMcnRules },
402
+ },
403
+
404
+ /**
405
+ * Recommended MCN config: both AMPscript and SSJS for standalone files.
406
+ * AMPscript portion flags MCN-unsupported functions; SSJS portion flags all SSJS as unsupported.
407
+ */
408
+ 'recommended-next': [
409
+ {
410
+ name: 'sfmc/recommended-next-ampscript',
411
+ plugins: { sfmc: plugin },
412
+ languageOptions: { parser: ampscriptParser },
413
+ files: ['**/*.ampscript', '**/*.amp'],
414
+ rules: {
415
+ ...ampRecommendedRules,
416
+ 'sfmc/amp-no-unknown-function': ['error', { target: 'next' }],
417
+ },
418
+ },
419
+ {
420
+ name: 'sfmc/recommended-next-ssjs',
421
+ plugins: { sfmc: plugin },
422
+ files: ['**/*.ssjs'],
423
+ languageOptions: {
424
+ ecmaVersion: 5,
425
+ sourceType: 'script',
426
+ globals: SSJS_GLOBALS_MAP,
427
+ },
428
+ rules: { ...ssjsMcnRules },
429
+ },
430
+ ],
431
+
432
+ /**
433
+ * MCN config for AMPscript and SSJS embedded in HTML files.
434
+ */
435
+ 'embedded-next': [
436
+ {
437
+ name: 'sfmc/embedded-next-processor',
438
+ plugins: { sfmc: plugin },
439
+ files: ['**/*.html'],
440
+ processor: 'sfmc/sfmc',
441
+ },
442
+ {
443
+ name: 'sfmc/embedded-next-ampscript-rules',
444
+ plugins: { sfmc: plugin },
445
+ languageOptions: { parser: ampscriptParser },
446
+ files: ['**/*.html/*.amp'],
447
+ rules: {
448
+ ...ampRecommendedRules,
449
+ 'sfmc/amp-no-unknown-function': ['error', { target: 'next' }],
450
+ },
451
+ },
452
+ {
453
+ name: 'sfmc/embedded-next-ssjs-rules',
454
+ plugins: { sfmc: plugin },
455
+ files: ['**/*.html/*.js'],
456
+ languageOptions: {
457
+ ecmaVersion: 5,
458
+ sourceType: 'script',
459
+ globals: SSJS_GLOBALS_MAP,
460
+ },
461
+ rules: { ...ssjsMcnRules },
462
+ },
463
+ ],
464
+
465
+ /**
466
+ * Strict MCN config: all rules at error severity for standalone + embedded.
467
+ */
468
+ 'strict-next': [
469
+ {
470
+ name: 'sfmc/strict-next-ampscript',
471
+ plugins: { sfmc: plugin },
472
+ languageOptions: { parser: ampscriptParser },
473
+ files: ['**/*.ampscript', '**/*.amp'],
474
+ rules: {
475
+ ...ampStrictRules,
476
+ 'sfmc/amp-no-unknown-function': ['error', { target: 'next' }],
477
+ },
478
+ },
479
+ {
480
+ name: 'sfmc/strict-next-ssjs',
481
+ plugins: { sfmc: plugin },
482
+ files: ['**/*.ssjs'],
483
+ languageOptions: {
484
+ ecmaVersion: 5,
485
+ sourceType: 'script',
486
+ globals: SSJS_GLOBALS_MAP,
487
+ },
488
+ rules: { ...ssjsMcnRules },
489
+ },
490
+ {
491
+ name: 'sfmc/strict-next-embedded-processor',
492
+ plugins: { sfmc: plugin },
493
+ files: ['**/*.html'],
494
+ processor: 'sfmc/sfmc',
495
+ },
496
+ {
497
+ name: 'sfmc/strict-next-embedded-ampscript-rules',
498
+ plugins: { sfmc: plugin },
499
+ languageOptions: { parser: ampscriptParser },
500
+ files: ['**/*.html/*.amp'],
501
+ rules: {
502
+ ...ampStrictRules,
503
+ 'sfmc/amp-no-unknown-function': ['error', { target: 'next' }],
504
+ },
505
+ },
506
+ {
507
+ name: 'sfmc/strict-next-embedded-ssjs-rules',
508
+ plugins: { sfmc: plugin },
509
+ files: ['**/*.html/*.js'],
510
+ languageOptions: {
511
+ ecmaVersion: 5,
512
+ sourceType: 'script',
513
+ globals: SSJS_GLOBALS_MAP,
514
+ },
515
+ rules: { ...ssjsMcnRules },
516
+ },
517
+ ],
343
518
  });
344
519
 
345
520
  export default plugin;
@@ -1,4 +1,4 @@
1
- import { functionNames } from 'ampscript-data';
1
+ import { functionNames, isMcnSupported } from 'ampscript-data';
2
2
 
3
3
  export default {
4
4
  meta: {
@@ -11,19 +11,47 @@ export default {
11
11
  messages: {
12
12
  unknownFunction:
13
13
  "'{{name}}' is not a recognized AMPscript function. AMPscript does not support custom functions.",
14
+ notSupportedInMcn: "'{{name}}' is not supported in Marketing Cloud Next.",
14
15
  },
15
- schema: [],
16
+ schema: [
17
+ {
18
+ type: 'object',
19
+ properties: {
20
+ target: {
21
+ type: 'string',
22
+ enum: ['engagement', 'next'],
23
+ description:
24
+ "Target platform. Set to 'next' to additionally flag functions not available in Marketing Cloud Next.",
25
+ },
26
+ },
27
+ additionalProperties: false,
28
+ },
29
+ ],
16
30
  },
17
31
 
18
32
  create(context) {
33
+ const options = context.options[0] ?? {};
34
+ const targetNext = options.target === 'next';
35
+
19
36
  return {
20
37
  FunctionCall(node) {
21
- if (!functionNames.has(node.name.toLowerCase())) {
38
+ const lower = node.name.toLowerCase();
39
+
40
+ if (!functionNames.has(lower)) {
22
41
  context.report({
23
42
  node,
24
43
  messageId: 'unknownFunction',
25
44
  data: { name: node.name },
26
45
  });
46
+ return;
47
+ }
48
+
49
+ if (targetNext && !isMcnSupported(lower)) {
50
+ context.report({
51
+ node,
52
+ messageId: 'notSupportedInMcn',
53
+ data: { name: node.name },
54
+ });
27
55
  }
28
56
  },
29
57
  };
@@ -49,11 +49,29 @@ export default {
49
49
  unknownCoreMethod:
50
50
  "'{{method}}' is not a known method of {{objectType}}. Available methods: {{available}}.",
51
51
  unknownWsproxyMethod: "'{{name}}' is not a recognized WSProxy method.",
52
+ ssjsNotSupportedInMcn:
53
+ 'SSJS is not supported in Marketing Cloud Next. Use AMPscript instead.',
52
54
  },
53
- schema: [],
55
+ schema: [
56
+ {
57
+ type: 'object',
58
+ properties: {
59
+ target: {
60
+ type: 'string',
61
+ enum: ['engagement', 'next'],
62
+ description:
63
+ "Target platform. Set to 'next' to flag all SSJS API calls as unsupported in Marketing Cloud Next.",
64
+ },
65
+ },
66
+ additionalProperties: false,
67
+ },
68
+ ],
54
69
  },
55
70
 
56
71
  create(context) {
72
+ const options = context.options[0] ?? {};
73
+ const targetNext = options.target === 'next';
74
+
57
75
  // Track variable name → Core Library type name (assigned via TypeName.Init())
58
76
  const coreVariables = new Map();
59
77
  // Track variable names assigned via new Script.Util.WSProxy()
@@ -100,6 +118,36 @@ export default {
100
118
 
101
119
  const methodName = property.name;
102
120
 
121
+ // ── MCN target: flag all SSJS API calls as unsupported ────────
122
+ if (targetNext) {
123
+ // Only report on Platform.<NS>.<method>(), HTTP.<method>(),
124
+ // Core Library instance calls, or WSProxy calls — not bare JS.
125
+ const isPlatformCall =
126
+ callee.object.type === 'MemberExpression' &&
127
+ callee.object.object.type === 'Identifier' &&
128
+ callee.object.object.name === 'Platform';
129
+ const isHttpCall =
130
+ callee.object.type === 'Identifier' && callee.object.name === 'HTTP';
131
+
132
+ // Traverse the object chain to find the root identifier
133
+ // (handles de.Rows.Retrieve() where root is `de`)
134
+ let rootNode = callee.object;
135
+ while (rootNode.type === 'MemberExpression') {
136
+ rootNode = rootNode.object;
137
+ }
138
+ const isInstanceCall =
139
+ rootNode.type === 'Identifier' &&
140
+ (coreVariables.has(rootNode.name) || wsproxyVariables.has(rootNode.name));
141
+
142
+ if (isPlatformCall || isHttpCall || isInstanceCall) {
143
+ context.report({
144
+ node: property,
145
+ messageId: 'ssjsNotSupportedInMcn',
146
+ });
147
+ return;
148
+ }
149
+ }
150
+
103
151
  // ── Platform.<NS>.<method>() ──────────────────────────────────
104
152
  if (
105
153
  callee.object.type === 'MemberExpression' &&
@@ -62,7 +62,7 @@ export default {
62
62
 
63
63
  create(context) {
64
64
  const coreVars = new Map(); // varName → className (Core Library instances)
65
- const wsproxyVars = new Set(); // varNames assigned new WSProxy()
65
+ const wsproxyVars = new Set(); // varNames assigned new Script.Util.WSProxy()
66
66
 
67
67
  function checkArgs(entry, args, callName) {
68
68
  if (!entry || !entry.params || entry.params.length === 0) {