eslint-plugin-sfmc 1.0.2 → 2.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.
@@ -4,22 +4,20 @@
4
4
  * In for-in loops, require a hasOwnProperty guard to avoid iterating
5
5
  * over inherited properties (like _type) that SSJS objects may have.
6
6
  *
7
- * Suggestion: wraps the existing loop body in an
7
+ * Auto-fix: wraps the existing loop body in an
8
8
  * `if (obj.hasOwnProperty(key)) { ... }` guard.
9
9
  */
10
10
 
11
11
  export default {
12
12
  meta: {
13
13
  type: 'suggestion',
14
- hasSuggestions: true,
14
+ fixable: 'code',
15
15
  docs: {
16
16
  description: 'Require hasOwnProperty guard in for-in loops',
17
17
  },
18
18
  messages: {
19
19
  missingGuard:
20
20
  'Add a hasOwnProperty check inside for-in loops to avoid iterating over inherited properties.',
21
- suggestAddGuard:
22
- 'Wrap the loop body in an `if ({{obj}}.hasOwnProperty({{key}})) { ... }` guard',
23
21
  },
24
22
  schema: [],
25
23
  },
@@ -41,35 +39,58 @@ export default {
41
39
  const keyName = getKeyName(node.left);
42
40
  const objText = context.sourceCode.getText(node.right);
43
41
 
42
+ if (!keyName) {
43
+ context.report({
44
+ node,
45
+ messageId: 'missingGuard',
46
+ });
47
+ return;
48
+ }
49
+
44
50
  context.report({
45
51
  node,
46
52
  messageId: 'missingGuard',
47
- suggest: keyName
48
- ? [
49
- {
50
- messageId: 'suggestAddGuard',
51
- data: { obj: objText, key: keyName },
52
- fix(fixer) {
53
- if (body.type === 'BlockStatement') {
54
- // Wrap the inner content of the existing block.
55
- const inner = context.sourceCode
56
- .getText(body)
57
- .slice(1, -1);
58
- return fixer.replaceText(
59
- body,
60
- `{ if (${objText}.hasOwnProperty(${keyName})) {${inner}} }`,
61
- );
62
- }
63
- // Single-statement body — create a new block with guard.
64
- const stmtText = context.sourceCode.getText(body);
65
- return fixer.replaceText(
66
- body,
67
- `{ if (${objText}.hasOwnProperty(${keyName})) { ${stmtText} } }`,
68
- );
69
- },
70
- },
71
- ]
72
- : [],
53
+ fix(fixer) {
54
+ const sourceCode = context.sourceCode;
55
+ const innerStmts = body.type === 'BlockStatement' ? body.body : [body];
56
+
57
+ // Single-line loops keep compact output to avoid reformatting
58
+ // code the author intentionally wrote on one line.
59
+ const isSingleLine = node.loc.start.line === node.loc.end.line;
60
+ if (isSingleLine) {
61
+ const compactBody = innerStmts
62
+ .map((stmt) => sourceCode.getText(stmt))
63
+ .join(' ');
64
+ return fixer.replaceText(
65
+ body,
66
+ `{ if (${objText}.hasOwnProperty(${keyName})) { ${compactBody} } }`,
67
+ );
68
+ }
69
+
70
+ // Multi-line loops: emit a properly-indented guard block.
71
+ const loopLine = sourceCode.lines[node.loc.start.line - 1] ?? '';
72
+ const baseIndent = (loopLine.match(/^[\t ]*/) ?? [''])[0];
73
+ const firstStmt = innerStmts[0];
74
+ const firstStmtLine = firstStmt
75
+ ? (sourceCode.lines[firstStmt.loc.start.line - 1] ?? '')
76
+ : '';
77
+ const firstStmtIndent = (firstStmtLine.match(/^[\t ]*/) ?? [''])[0];
78
+ const unit =
79
+ firstStmtIndent.length > baseIndent.length
80
+ ? firstStmtIndent.slice(baseIndent.length)
81
+ : ' ';
82
+ const guardIndent = baseIndent + unit;
83
+ const stmtIndent = guardIndent + unit;
84
+
85
+ const guardedBody = innerStmts
86
+ .map((stmt) => `${stmtIndent}${sourceCode.getText(stmt)}`)
87
+ .join('\n');
88
+
89
+ return fixer.replaceText(
90
+ body,
91
+ `{\n${guardIndent}if (${objText}.hasOwnProperty(${keyName})) {\n${guardedBody}\n${guardIndent}}\n${baseIndent}}`,
92
+ );
93
+ },
73
94
  });
74
95
  }
75
96
  },