eslint-plugin-stratified-design 0.12.9 → 0.12.11

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.
@@ -127,3 +127,10 @@ const func = () => "a";
127
127
  // @data
128
128
  const obj = { a: func() };
129
129
  ```
130
+
131
+ ```js
132
+ import { lib } from "module";
133
+ //@import
134
+ const { fn2 } = lib;
135
+ const fn1 = () => fn2();
136
+ ```
@@ -98,7 +98,9 @@ const validateRawStructure = (rawStructure) => {
98
98
  for (const rawLayer of rawLayers) {
99
99
  if (!isRawLayerValid(rawLayer)) return false;
100
100
  const layerName = typeof rawLayer === "string" ? rawLayer : rawLayer.name;
101
- if (toSegments(layerName).length > 1) return false;
101
+ const isNodeModule =
102
+ typeof rawLayer === "string" ? false : rawLayer.nodeModule;
103
+ if (!isNodeModule && toSegments(layerName).length > 1) return false;
102
104
  }
103
105
  }
104
106
  return true;
@@ -17,6 +17,7 @@ const { fromCwd, match, or } = require("../helpers/common");
17
17
  * @typedef {import('eslint').AST.Token} Token
18
18
  * @typedef {import('eslint').SourceCode} SourceCode
19
19
  * @typedef {({[name: string]: number})} Levels
20
+ * @typedef {({[name: string]: boolean})} DataNames
20
21
  */
21
22
 
22
23
  /**
@@ -35,7 +36,12 @@ const deriveDeclaration = (nodeOrToken) => {
35
36
  );
36
37
  };
37
38
 
38
- const isDataOrImported = (declarationInit, levels) => {
39
+ /**
40
+ * @param {any} declarationInit
41
+ * @param {DataNames} dataNames
42
+ * @returns {boolean}
43
+ */
44
+ const isData = (declarationInit, dataNames) => {
39
45
  const expression = or(
40
46
  () => declarationInit.expression,
41
47
  () => declarationInit
@@ -46,39 +52,45 @@ const isDataOrImported = (declarationInit, levels) => {
46
52
  if (type === "Literal") return true;
47
53
  if (type === "Identifier") {
48
54
  const name = or(() => expression.name);
49
- return name && !levels[name];
55
+ return name && dataNames[name];
50
56
  }
51
57
  if (type === "MemberExpression") {
52
58
  const name = or(() => expression.object.name);
53
- return name && !levels[name];
59
+ return name && dataNames[name];
54
60
  }
55
61
  if (type === "CallExpression") {
56
62
  const name = or(() => expression.callee.name);
57
- return name && !levels[name];
63
+ return name && dataNames[name];
58
64
  }
59
65
  if (type === "JSXElement") {
60
66
  const name = or(() => expression.openingElement.name.name);
61
- return name && !levels[name];
67
+ return name && dataNames[name];
62
68
  }
63
- if (type === "SpreadElement")
64
- return isDataOrImported(expression.argument, levels);
69
+ if (type === "SpreadElement") return isData(expression.argument, dataNames);
65
70
  if (type === "ArrayExpression")
66
- return expression.elements.every((init) => isDataOrImported(init, levels));
71
+ return expression.elements.every((init) => isData(init, dataNames));
67
72
  if (type === "ObjectExpression")
68
- return expression.properties.every(({ value }) =>
69
- isDataOrImported(value, levels)
70
- );
73
+ return expression.properties.every(({ value }) => isData(value, dataNames));
71
74
  return false;
72
75
  };
73
76
 
77
+ /**
78
+ * @param {Node | Token} nodeOrToken
79
+ * @param {DataNames} dataNames
80
+ * @returns {boolean}
81
+ */
82
+ const isNodeData = (nodeOrToken, dataNames) => {
83
+ const declaration = deriveDeclaration(nodeOrToken);
84
+ return isData(declaration.init, dataNames);
85
+ };
86
+
74
87
  /**
75
88
  * @param {Node | Token} nodeOrToken
76
89
  * @param {Levels} levels
77
90
  * @returns {string[] | undefined}
78
91
  */
79
- const deriveNames = (nodeOrToken, levels) => {
92
+ const deriveNames = (nodeOrToken) => {
80
93
  const declaration = deriveDeclaration(nodeOrToken);
81
- if (isDataOrImported(declaration.init, levels)) return [];
82
94
  const names = or(
83
95
  () => declaration.id.name,
84
96
  () => declaration.id.elements.map(({ name }) => name),
@@ -97,12 +109,37 @@ const deriveCallerName = (nodeOrToken) =>
97
109
  /**
98
110
  * @param {SourceCode} sourceCode
99
111
  * @param {Node | Token} nodeOrToken
100
- * @returns {number | null}
112
+ * @returns {boolean}
113
+ */
114
+ const hasDataComment = (sourceCode, nodeOrToken) => {
115
+ const comments = sourceCode.getCommentsBefore(nodeOrToken);
116
+ for (const { value: comment } of comments) {
117
+ if (comment.includes("@data")) return true;
118
+ }
119
+ return false;
120
+ };
121
+
122
+ /**
123
+ * @param {SourceCode} sourceCode
124
+ * @param {Node | Token} nodeOrToken
125
+ * @returns {boolean}
126
+ */
127
+ const hasImportComment = (sourceCode, nodeOrToken) => {
128
+ const comments = sourceCode.getCommentsBefore(nodeOrToken);
129
+ for (const { value: comment } of comments) {
130
+ if (comment.includes("@import")) return true;
131
+ }
132
+ return false;
133
+ };
134
+
135
+ /**
136
+ * @param {SourceCode} sourceCode
137
+ * @param {Node | Token} nodeOrToken
138
+ * @returns {number}
101
139
  */
102
140
  const deriveLevel = (sourceCode, nodeOrToken) => {
103
141
  const comments = sourceCode.getCommentsBefore(nodeOrToken);
104
142
  for (const { value: comment } of comments) {
105
- if (comment.includes("@data") || comment.includes("@import")) return null;
106
143
  const levelInStr = comment.replace(/^[^]*@level\s+?([0-9]+)[^0-9]*$/, "$1");
107
144
  const levelInNum = Number(levelInStr);
108
145
  if (levelInStr && !Number.isNaN(levelInNum)) {
@@ -208,6 +245,12 @@ module.exports = {
208
245
  */
209
246
  const levels = {};
210
247
 
248
+ /**
249
+ * 선언된 것이 데이터인지 여부를 나타내는 객체.
250
+ * @type {DataNames}
251
+ */
252
+ const dataNames = {};
253
+
211
254
  const sourceCode = context.sourceCode;
212
255
 
213
256
  /**
@@ -228,9 +271,17 @@ module.exports = {
228
271
  return {
229
272
  Program(node) {
230
273
  node.body.forEach((token) => {
231
- deriveNames(token, levels).forEach((name) => {
274
+ deriveNames(token).forEach((name) => {
275
+ if (!name || hasImportComment(sourceCode, token)) return;
276
+ if (
277
+ hasDataComment(sourceCode, token) ||
278
+ isNodeData(token, dataNames)
279
+ ) {
280
+ dataNames[name] = true;
281
+ return;
282
+ }
232
283
  const level = deriveLevel(sourceCode, token);
233
- if (name && level) levels[name] = level;
284
+ levels[name] = level;
234
285
  });
235
286
  });
236
287
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-stratified-design",
3
- "version": "0.12.9",
3
+ "version": "0.12.11",
4
4
  "description": "ESlint rules for stratified design",
5
5
  "keywords": [
6
6
  "eslint",
@@ -147,6 +147,13 @@ describe("helpers/stratified-imports", () => {
147
147
  ["layerC"],
148
148
  ],
149
149
  },
150
+ {
151
+ expected: true,
152
+ rawStructure: [
153
+ ["layerA", { name: "layerA/layerB", nodeModule: true }],
154
+ ["layerC"],
155
+ ],
156
+ },
150
157
  {
151
158
  expected: true,
152
159
  rawStructure: [
@@ -268,35 +268,7 @@ ruleTester.run("no-same-level-funcs", rule, {
268
268
  filename: "./src/foo.js",
269
269
  },
270
270
  {
271
- code: "import { lib } from 'lib'; const { fn2 } = lib; const fn1 = () => fn2()",
272
- filename: "./src/foo.js",
273
- },
274
- {
275
- code: "import { lib } from 'lib'; const fn2 = lib.fn2; const fn1 = () => fn2()",
276
- filename: "./src/foo.js",
277
- },
278
- {
279
- code: "import { fn2 } from 'lib'; const arr = [fn2]; const fn1 = () => arr[0]()",
280
- filename: "./src/foo.js",
281
- },
282
- {
283
- code: "import { fn2 } from 'lib'; const arr = [fn2()]; const fn1 = () => arr.push(1)",
284
- filename: "./src/foo.js",
285
- },
286
- {
287
- code: "import { fn2 } from 'lib'; const data = [...fn2()]; const fn1 = () => data.push(1)",
288
- filename: "./src/foo.js",
289
- },
290
- {
291
- code: "import { fn2 } from 'lib'; const arr = [fn2]; const fn1 = () => arr.push(1)",
292
- filename: "./src/foo.js",
293
- },
294
- {
295
- code: "import { Comp2 } from 'lib'; const arr = [<Comp2 />]; const fn1 = () => arr.push(1)",
296
- filename: "./src/foo.js",
297
- },
298
- {
299
- code: "import { Comp2 } from 'lib'; const arr = [Comp2]; const fn1 = () => arr.push(1)",
271
+ code: "import { lib } from 'lib'; /*@import*/const { fn2 } = lib; const fn1 = () => fn2()",
300
272
  filename: "./src/foo.js",
301
273
  },
302
274
  ],
@@ -483,5 +455,51 @@ ruleTester.run("no-same-level-funcs", rule, {
483
455
  filename: "./src/foo.js",
484
456
  errors: [{ messageId: "no-same-level-funcs", data: { func: "__fn2" } }],
485
457
  },
458
+
459
+ {
460
+ code: "import { lib } from 'lib'; const { fn2 } = lib; const fn1 = () => fn2()",
461
+ filename: "./src/foo.js",
462
+ errors: [{ messageId: "no-same-level-funcs", data: { func: "fn2" } }],
463
+ },
464
+ {
465
+ code: "import { lib } from 'lib'; const fn2 = lib.fn2; const fn1 = () => fn2()",
466
+ filename: "./src/foo.js",
467
+ errors: [{ messageId: "no-same-level-funcs", data: { func: "fn2" } }],
468
+ },
469
+ {
470
+ code: "import { fn2 } from 'lib'; const arr = [fn2]; const fn1 = () => arr[0]()",
471
+ filename: "./src/foo.js",
472
+ errors: [{ messageId: "no-same-level-funcs", data: { func: "arr" } }],
473
+ },
474
+ {
475
+ code: "import { hof1, hof2 } from 'lib'; const fn2 = hof2(); const fn1 = () => hof1(fn2)",
476
+ filename: "./src/foo.js",
477
+ errors: [{ messageId: "no-same-level-funcs", data: { func: "fn2" } }],
478
+ },
479
+ {
480
+ code: "import { fn2 } from 'lib'; const arr = [fn2()]; const fn1 = () => arr.push(1)",
481
+ filename: "./src/foo.js",
482
+ errors: [{ messageId: "no-same-level-funcs", data: { func: "arr" } }],
483
+ },
484
+ {
485
+ code: "import { fn2 } from 'lib'; const data = [...fn2()]; const fn1 = () => data.push(1)",
486
+ filename: "./src/foo.js",
487
+ errors: [{ messageId: "no-same-level-funcs", data: { func: "data" } }],
488
+ },
489
+ {
490
+ code: "import { fn2 } from 'lib'; const arr = [fn2]; const fn1 = () => arr.push(1)",
491
+ filename: "./src/foo.js",
492
+ errors: [{ messageId: "no-same-level-funcs", data: { func: "arr" } }],
493
+ },
494
+ {
495
+ code: "import { Comp2 } from 'lib'; const arr = [<Comp2 />]; const fn1 = () => arr.push(1)",
496
+ filename: "./src/foo.js",
497
+ errors: [{ messageId: "no-same-level-funcs", data: { func: "arr" } }],
498
+ },
499
+ {
500
+ code: "import { Comp2 } from 'lib'; const arr = [Comp2]; const fn1 = () => arr.push(1)",
501
+ filename: "./src/foo.js",
502
+ errors: [{ messageId: "no-same-level-funcs", data: { func: "arr" } }],
503
+ },
486
504
  ],
487
505
  });