eslint-plugin-stratified-design 0.12.5 → 0.12.6

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.
@@ -20,36 +20,97 @@ const { fromCwd, match } = require("../helpers/common");
20
20
  * @typedef {({[name: string]: number})} Levels
21
21
  */
22
22
 
23
+ const hasArrFunction = (elements, levels) => {
24
+ return elements.reduce((hasFunc, element) => {
25
+ if (hasFunc) return true;
26
+ const type = element.type;
27
+ if (type === "FunctionExpression" || type === "ArrowFunctionExpression")
28
+ return true;
29
+ if (type === "ObjectExpression")
30
+ return hasObjFunction(element.properties, levels);
31
+ if (type === "ArrayExpression")
32
+ return hasArrFunction(element.elements, levels);
33
+ if ("name" in element && element.name in levels) return true;
34
+ return false;
35
+ }, false);
36
+ };
37
+
38
+ const hasObjFunction = (properties, levels) => {
39
+ return properties.reduce((hasFunc, property) => {
40
+ if (hasFunc) return true;
41
+ if (!("value" in property)) return false;
42
+ const type = property.value.type;
43
+ if (type === "FunctionExpression" || type === "ArrowFunctionExpression")
44
+ return true;
45
+ if (type === "ObjectExpression")
46
+ return hasObjFunction(property.value.properties, levels);
47
+ if (type === "ArrayExpression")
48
+ return hasArrFunction(property.value.elements, levels);
49
+ if ("name" in property.value && property.value.name in levels) return true;
50
+ return false;
51
+ }, false);
52
+ };
53
+
54
+ /**
55
+ * @param {Node | Token} nodeOrToken
56
+ */
57
+ const deriveDeclaration = (nodeOrToken) => {
58
+ return (nodeOrToken.type === "ExportNamedDeclaration" ||
59
+ nodeOrToken.type === "ExportDefaultDeclaration") &&
60
+ Boolean(nodeOrToken.declaration)
61
+ ? nodeOrToken.declaration
62
+ : nodeOrToken;
63
+ };
64
+
23
65
  /**
24
66
  * @param {Node | Token} nodeOrToken
25
67
  * @returns {string | null}
26
68
  */
27
69
  const deriveFuncName = (nodeOrToken) => {
28
- const declaration =
29
- (nodeOrToken.type === "ExportNamedDeclaration" ||
30
- nodeOrToken.type === "ExportDefaultDeclaration") &&
31
- Boolean(nodeOrToken.declaration)
32
- ? nodeOrToken.declaration
33
- : nodeOrToken;
34
- const isFuncDeclaration = declaration.type === "FunctionDeclaration";
35
- const isVarDeclaration =
36
- declaration.type === "VariableDeclaration" &&
70
+ const declaration = deriveDeclaration(nodeOrToken);
71
+
72
+ if (declaration.type === "FunctionDeclaration") return declaration.id.name;
73
+ if (declaration.type !== "VariableDeclaration") return null;
74
+
75
+ const { init, id } = declaration.declarations[0];
76
+ if (
37
77
  [
38
78
  "ArrowFunctionExpression",
39
79
  "FunctionExpression",
40
80
  "CallExpression",
41
81
  "TaggedTemplateExpression",
42
- ].includes(declaration.declarations[0].init.type);
43
-
44
- if (isFuncDeclaration || isVarDeclaration) {
45
- const name = isFuncDeclaration
46
- ? declaration.id.name
47
- : declaration.declarations[0].id.name;
48
- return name;
82
+ ].includes(init.type)
83
+ ) {
84
+ return id.name;
49
85
  }
86
+
50
87
  return null;
51
88
  };
52
89
 
90
+ /**
91
+ * @param {Node | Token} nodeOrToken
92
+ * @param {Levels} levels
93
+ * @returns {string | null}
94
+ */
95
+ const deriveVarName = (nodeOrToken, levels) => {
96
+ const declaration = deriveDeclaration(nodeOrToken);
97
+ if (declaration.type !== "VariableDeclaration") return null;
98
+ const { init, id } = declaration.declarations[0];
99
+ if (
100
+ init.type === "ObjectExpression" &&
101
+ hasObjFunction(init.properties, levels)
102
+ ) {
103
+ return id.name;
104
+ } else if (
105
+ init.type === "ArrayExpression" &&
106
+ hasArrFunction(init.elements, levels)
107
+ ) {
108
+ return id.name;
109
+ } else {
110
+ return null;
111
+ }
112
+ };
113
+
53
114
  /**
54
115
  * @param {SourceCode} sourceCode
55
116
  * @param {Node | Token} nodeOrToken
@@ -89,16 +150,24 @@ const isCalleeLowerLevel = (sourceCode, levels) => {
89
150
  if (calleeLevel === undefined) return true; // 호출되는 함수가 존재하지 않으면 true 리턴. 함수 내의 함수의 경우에는 항상 calleeLevel 이 undefined 이다.
90
151
 
91
152
  const caller = traceAncestor(sourceCode, node);
92
- const callerLevel = deriveLevel(sourceCode, caller);
93
- if (callerLevel < calleeLevel) return true;
153
+ const callerName = deriveFuncName(caller) || deriveVarName(caller, levels);
154
+ const callerLevel = levels[callerName];
94
155
 
95
- const callerName = deriveFuncName(caller);
156
+ if (callerLevel === undefined) return true;
157
+ if (callerLevel < calleeLevel) return true;
96
158
  if (callerName === calleeName) return true; // recursive function
97
159
 
98
160
  return false;
99
161
  };
100
162
  };
101
163
 
164
+ const deriveCalleeName = (callee) => {
165
+ if (callee.type === "Identifier") return callee.name;
166
+ if (callee.type === "MemberExpression")
167
+ return deriveCalleeName(callee.object);
168
+ return null;
169
+ };
170
+
102
171
  /** @type {import('eslint').Rule.RuleModule} */
103
172
  module.exports = {
104
173
  meta: {
@@ -175,10 +244,11 @@ module.exports = {
175
244
  Program(node) {
176
245
  node.body.forEach((token) => {
177
246
  const name = deriveFuncName(token);
178
- if (name) {
179
- const level = deriveLevel(sourceCode, token);
180
- levels[name] = level;
181
- }
247
+ if (name !== null) levels[name] = deriveLevel(sourceCode, token);
248
+ });
249
+ node.body.forEach((token) => {
250
+ const name = deriveVarName(token, levels);
251
+ if (name !== null) levels[name] = deriveLevel(sourceCode, token);
182
252
  });
183
253
  },
184
254
  CallExpression(node) {
@@ -186,7 +256,7 @@ module.exports = {
186
256
  const isReported = report(node, name);
187
257
  if (isReported) return;
188
258
  }
189
- report(node, node.callee.name);
259
+ report(node, deriveCalleeName(node.callee));
190
260
  },
191
261
  JSXOpeningElement(node) {
192
262
  report(node, node.name.name);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-stratified-design",
3
- "version": "0.12.5",
3
+ "version": "0.12.6",
4
4
  "description": "ESlint rules for stratified design",
5
5
  "keywords": [
6
6
  "eslint",
@@ -158,6 +158,66 @@ ruleTester.run("no-same-level-funcs", rule, {
158
158
  code: "/*@level 2*/const func = () => 'a'; /*@level 1*/const obj = { func: () => { func() } } ",
159
159
  filename: "./src/foo.js",
160
160
  },
161
+
162
+ {
163
+ code: "const func = () => 'a'; const obj = { a: func() } ",
164
+ filename: "./src/foo.js",
165
+ },
166
+ {
167
+ code: "/*@level 2*/const func = () => 'a'; /*@level 1*/const obj = { func: () => { func() } } ",
168
+ filename: "./src/foo.js",
169
+ errors: [{ messageId: "no-same-level-funcs", data: { func: "obj" } }],
170
+ },
171
+ {
172
+ code: "/*@level 2*/const obj = { func2: () => {} }; /*@level 1*/const func1 = () => { obj.func2() } ",
173
+ filename: "./src/foo.js",
174
+ },
175
+ {
176
+ code: "/*@level 2*/const obj = { func2(){} }; /*@level 1*/const func1 = () => { obj.func2() } ",
177
+ filename: "./src/foo.js",
178
+ },
179
+ {
180
+ code: "/*@level 3*/const func3 = () => 'a'; /*@level 2*/const obj = { func2: func3 }; /*@level 1*/const func1 = () => { obj.func2() } ",
181
+ filename: "./src/foo.js",
182
+ errors: [{ messageId: "no-same-level-funcs", data: { func: "obj" } }],
183
+ },
184
+
185
+ {
186
+ code: "const func = () => 'a'; const arr = [func()];",
187
+ filename: "./src/foo.js",
188
+ },
189
+ {
190
+ code: "/*@level 2*/const func = () => 'a'; /*@level 1*/const arr = [() => { func() }];",
191
+ filename: "./src/foo.js",
192
+ },
193
+ {
194
+ code: "/*@level 2*/const arr = [() => {}]; /*@level 1*/const func1 = () => { arr[0]() } ",
195
+ filename: "./src/foo.js",
196
+ },
197
+ {
198
+ code: "/*@level 3*/const func3 = () => 'a'; /*@level 2*/const arr = [func3]; /*@level 1*/const func1 = () => { arr[0]() } ",
199
+ filename: "./src/foo.js",
200
+ },
201
+ {
202
+ code: "import { func } from './module'; const arr = [{a: {b: {c: [func]}}}];",
203
+ filename: "./src/foo.js",
204
+ },
205
+ {
206
+ code: "const func = () => {}; const arr = [{a: {b: {c: [func]}}}];",
207
+ filename: "./src/foo.js",
208
+ },
209
+ {
210
+ code: "/*@level 3*/const func3 = () => {}; /*@level 2*/const arr = [{a: {b: {c: [func3]}}}]; /*@level 1*/const func1 = () => arr[0].a.b.c[0]();",
211
+ filename: "./src/foo.js",
212
+ },
213
+ {
214
+ code: "const obj2 = {}; const obj1 = {...obj2};",
215
+ filename: "./src/foo.js",
216
+ },
217
+ {
218
+ code: "const arr2 = []; const arr1 = [...arr2];",
219
+ filename: "./src/foo.js",
220
+ },
161
221
  ],
162
222
  invalid: [
163
223
  {
@@ -175,13 +235,11 @@ ruleTester.run("no-same-level-funcs", rule, {
175
235
  filename: "./src/foo.js",
176
236
  errors: [{ messageId: "no-same-level-funcs", data: { func: "func1" } }],
177
237
  },
178
-
179
238
  {
180
239
  code: "function func2(){ func1(); }; function func1(){}",
181
240
  filename: "./src/foo.js",
182
241
  errors: [{ messageId: "no-same-level-funcs", data: { func: "func1" } }],
183
242
  },
184
-
185
243
  {
186
244
  code: "export function func2(){ func1(); }; function func1(){}",
187
245
  filename: "./src/foo.js",
@@ -192,7 +250,6 @@ ruleTester.run("no-same-level-funcs", rule, {
192
250
  filename: "./src/foo.js",
193
251
  errors: [{ messageId: "no-same-level-funcs", data: { func: "func1" } }],
194
252
  },
195
-
196
253
  {
197
254
  code: "const func1 = () => {}; const func2 = () => { func1(); }",
198
255
  filename: "./src/foo.js",
@@ -203,7 +260,6 @@ ruleTester.run("no-same-level-funcs", rule, {
203
260
  filename: "./src/foo.js",
204
261
  errors: [{ messageId: "no-same-level-funcs", data: { func: "func1" } }],
205
262
  },
206
-
207
263
  {
208
264
  code: "const func1 = function(){}; const func2 = function(){ func1(); }",
209
265
  filename: "./src/foo.js",
@@ -214,7 +270,6 @@ ruleTester.run("no-same-level-funcs", rule, {
214
270
  filename: "./src/foo.js",
215
271
  errors: [{ messageId: "no-same-level-funcs", data: { func: "func1" } }],
216
272
  },
217
-
218
273
  {
219
274
  code: "const func1 = function func1(){}; const func2 = function func2(){ func1(); }",
220
275
  filename: "./src/foo.js",
@@ -225,7 +280,6 @@ ruleTester.run("no-same-level-funcs", rule, {
225
280
  filename: "./src/foo.js",
226
281
  errors: [{ messageId: "no-same-level-funcs", data: { func: "func1" } }],
227
282
  },
228
-
229
283
  {
230
284
  code: "const fn = () => 1; const value = fn()",
231
285
  filename: "./src/foo.js",
@@ -276,7 +330,6 @@ ruleTester.run("no-same-level-funcs", rule, {
276
330
  filename: "./src/foo.js",
277
331
  errors: [{ messageId: "no-same-level-funcs", data: { func: "func1" } }],
278
332
  },
279
-
280
333
  {
281
334
  code: "function func2(){};\n// @level 1\nfunction func1(){ func2(); }",
282
335
  filename: "./src/foo.js",
@@ -284,14 +337,45 @@ ruleTester.run("no-same-level-funcs", rule, {
284
337
  },
285
338
 
286
339
  {
287
- code: "const func = () => 'a'; const obj = { a: func() } ",
340
+ code: "const func = () => 'a'; const obj = { func: () => { func() } };",
288
341
  filename: "./src/foo.js",
289
342
  errors: [{ messageId: "no-same-level-funcs", data: { func: "func" } }],
290
343
  },
291
344
  {
292
- code: "const func = () => 'a'; const obj = { func: () => { func() } } ",
345
+ code: "const obj = { func2: () => {} }; const func1 = () => { obj.func2() } ",
346
+ filename: "./src/foo.js",
347
+ errors: [{ messageId: "no-same-level-funcs", data: { func: "obj" } }],
348
+ },
349
+ {
350
+ code: "const obj = { func2(){} }; const func1 = () => { obj.func2() } ",
351
+ filename: "./src/foo.js",
352
+ errors: [{ messageId: "no-same-level-funcs", data: { func: "obj" } }],
353
+ },
354
+ {
355
+ code: "const func3 = () => 'a'; const obj = { func2: func3 }; const func1 = () => { obj.func2() } ",
356
+ filename: "./src/foo.js",
357
+ errors: [{ messageId: "no-same-level-funcs", data: { func: "obj" } }],
358
+ },
359
+
360
+ {
361
+ code: "const func = () => 'a'; const arr = [() => { func() }];",
293
362
  filename: "./src/foo.js",
294
363
  errors: [{ messageId: "no-same-level-funcs", data: { func: "func" } }],
295
364
  },
365
+ {
366
+ code: "const arr = [() => {}]; const func1 = () => { arr[0]() } ",
367
+ filename: "./src/foo.js",
368
+ errors: [{ messageId: "no-same-level-funcs", data: { func: "arr" } }],
369
+ },
370
+ {
371
+ code: "const func3 = () => 'a'; const arr = [func3]; const func1 = () => { arr[0]() } ",
372
+ filename: "./src/foo.js",
373
+ errors: [{ messageId: "no-same-level-funcs", data: { func: "arr" } }],
374
+ },
375
+ {
376
+ code: "const func3 = () => {}; const arr = [{a: {b: {c: [func3]}}}]; const func1 = () => arr[0].a.b.c[0]();",
377
+ filename: "./src/foo.js",
378
+ errors: [{ messageId: "no-same-level-funcs", data: { func: "arr" } }],
379
+ },
296
380
  ],
297
381
  });