eslint-plugin-stratified-design 0.9.0 → 0.9.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.
@@ -16,17 +16,17 @@ To control module imports, use the `imports` option:
16
16
  {
17
17
  "imports": [
18
18
  {
19
- "import": { "member": ["foo"], "from": "**/src/fileA" },
20
- "allow": ["**/src/some-dir/*.js"]
19
+ "import": { "member": ["foo"], "from": "src/fileA" },
20
+ "allow": ["src/some-dir/*.js"]
21
21
  },
22
22
  {
23
- "import": { "member": ["foo", "bar"], "from": "**/src/fileB" },
24
- "disallow": ["**/src/not-allowed-file.js"]
23
+ "import": { "member": ["foo", "bar"], "from": "src/fileB" },
24
+ "disallow": ["src/not-allowed-file.js"]
25
25
  },
26
26
  {
27
- "import": { "member": ["baz"], "from": "**/src/fileC" },
28
- "allow": ["**/src/some-dir/*.js"],
29
- "disallow": ["**/src/not-allowed-file.js"]
27
+ "import": { "member": ["baz"], "from": "src/fileC" },
28
+ "allow": ["src/some-dir/*.js"],
29
+ "disallow": ["src/not-allowed-file.js"]
30
30
  }
31
31
  ]
32
32
  }
@@ -57,9 +57,9 @@ Examples of **incorrect** code for this rule:
57
57
  // {
58
58
  // "imports": [
59
59
  // {
60
- // "import": { "member": ["foo"], "from": "**/src/fileA" },
61
- // "allow": ["**/src/**/fileB.js"],
62
- // "disallow": ["**/src/**/fileC.*"]
60
+ // "import": { "member": ["foo"], "from": "src/fileA" },
61
+ // "allow": ["src/**/fileB.js"],
62
+ // "disallow": ["src/**/fileC.*"]
63
63
  // }
64
64
  // ]
65
65
  // }
@@ -77,9 +77,9 @@ Examples of **correct** code for this rule:
77
77
  // {
78
78
  // "imports": [
79
79
  // {
80
- // "import": { "member": ["foo"], "from": "**/src/fileA" },
81
- // "allow": ["**/src/**/fileB.js"],
82
- // "disallow": ["**/src/**/fileC.*"]
80
+ // "import": { "member": ["foo"], "from": "src/fileA" },
81
+ // "allow": ["src/**/fileB.js"],
82
+ // "disallow": ["src/**/fileC.*"]
83
83
  // }
84
84
  // ]
85
85
  // }
@@ -1,3 +1,4 @@
1
+ const { minimatch } = require("minimatch");
1
2
  const p = require("path");
2
3
 
3
4
  /**
@@ -116,6 +117,23 @@ const replaceAlias = (cwd, fileDir, aliases) => {
116
117
  };
117
118
  };
118
119
 
120
+ // @level 1
121
+ /**
122
+ * @param {string} cwd
123
+ * @param {string} path
124
+ */
125
+ const fromCwd = (cwd, path) => p.relative(cwd, path);
126
+
127
+ /**
128
+ * @param {string} path
129
+ */
130
+ const match = (path) => {
131
+ /**
132
+ * @param {string} pattern
133
+ */
134
+ return (pattern) => minimatch(path, pattern);
135
+ };
136
+
119
137
  module.exports = {
120
138
  toRelative,
121
139
  toSegments,
@@ -129,4 +147,6 @@ module.exports = {
129
147
  reducedPaths,
130
148
  createAliases,
131
149
  replaceAlias,
150
+ fromCwd,
151
+ match,
132
152
  };
@@ -1,4 +1,3 @@
1
- const { minimatch } = require("minimatch");
2
1
  const { report: reportError } = require("./2 layer");
3
2
  const {
4
3
  isNodeModule,
@@ -12,6 +11,8 @@ const {
12
11
  toPath,
13
12
  resolvePath,
14
13
  parsePath,
14
+ fromCwd,
15
+ match,
15
16
  } = require("../common");
16
17
 
17
18
  const FINISHED = "finished";
@@ -28,19 +29,17 @@ const createRootDir = (cwd, options) => {
28
29
 
29
30
  /**
30
31
  * @param {import("./3 layer").Options} options
32
+ * @param {string} cwd
31
33
  * @param {string} contextFileSource
32
34
  */
33
- const parseFileSource = (options, contextFileSource) => {
35
+ const parseFileSource = (options, cwd, contextFileSource) => {
34
36
  const fileSource = resolvePath(contextFileSource);
35
37
  const parsedFileSource = parsePath(fileSource);
36
38
  const fileDir = resolvePath(parsedFileSource.dir);
37
39
  const filePath = resolvePath(fileDir, parsedFileSource.name);
38
- const isIncludedFile = options.include.find((pattern) =>
39
- minimatch(fileSource, pattern)
40
- );
41
- const isExcludedFile = options.exclude.find((pattern) =>
42
- minimatch(fileSource, pattern)
43
- );
40
+ const fileSourceFromCwd = fromCwd(cwd, fileSource);
41
+ const isIncludedFile = options.include.find(match(fileSourceFromCwd));
42
+ const isExcludedFile = options.exclude.find(match(fileSourceFromCwd));
44
43
  return {
45
44
  fileDir,
46
45
  filePath,
@@ -61,13 +60,21 @@ const createModulePath = (cwd, excludeImports, fileDir, aliases) => {
61
60
  */
62
61
  return (moduleSourceWithAlias) => {
63
62
  const moduleSource = removeAlias(moduleSourceWithAlias);
63
+
64
64
  const isNodeModule = moduleSource.startsWith(".") === false;
65
+
65
66
  const modulePath = isNodeModule
66
67
  ? moduleSource
67
68
  : resolvePath(fileDir, moduleSource);
69
+
70
+ const modulePathFromCwd = isNodeModule
71
+ ? modulePath
72
+ : fromCwd(cwd, modulePath);
73
+
68
74
  const isModuleExcluded = Boolean(
69
- excludeImports.find((pattern) => minimatch(modulePath, pattern))
75
+ excludeImports.find(match(modulePathFromCwd))
70
76
  );
77
+
71
78
  return { modulePath, isModuleExcluded };
72
79
  };
73
80
  };
@@ -1,11 +1,12 @@
1
1
  "use strict";
2
2
 
3
- const { minimatch } = require("minimatch");
4
3
  const {
5
4
  resolvePath,
6
5
  reducedPaths,
7
6
  parsePath,
8
7
  replaceAlias,
8
+ fromCwd,
9
+ match,
9
10
  } = require("../common");
10
11
  const {
11
12
  findLevel,
@@ -35,18 +36,20 @@ const BARRIER = "barrier";
35
36
  // @level 2
36
37
  /**
37
38
  * @param {Options} options
39
+ * @param {string} cwd
38
40
  * @param {string} contextFileSource
39
41
  */
40
- const parseFileSource = (options, contextFileSource) => {
42
+ const parseFileSource = (options, cwd, contextFileSource) => {
41
43
  const fileSource = resolvePath(contextFileSource);
42
44
  const parsedFileSource = parsePath(fileSource);
43
45
  const fileDir = resolvePath(parsedFileSource.dir);
44
46
  const filePath = resolvePath(fileDir, parsedFileSource.name);
47
+ const fileSourceFromCwd = fromCwd(cwd, fileSource);
45
48
  const isIncludedFile = Boolean(
46
- options.include.find((pattern) => minimatch(fileSource, pattern))
49
+ options.include.find(match(fileSourceFromCwd))
47
50
  );
48
51
  const isExcludedFile = Boolean(
49
- options.exclude.find((pattern) => minimatch(fileSource, pattern))
52
+ options.exclude.find(match(fileSourceFromCwd))
50
53
  );
51
54
  return {
52
55
  fileDir,
@@ -72,13 +75,21 @@ const createModulePath = (cwd, excludeImports, fileDir, aliases) => {
72
75
  fileDir,
73
76
  aliases
74
77
  )(moduleSourceWithAlias);
78
+
75
79
  const isNodeModule = moduleSource.startsWith(".") === false;
80
+
76
81
  const modulePath = isNodeModule
77
82
  ? moduleSource
78
83
  : resolvePath(fileDir, moduleSource);
84
+
85
+ const modulePathFromCwd = isNodeModule
86
+ ? modulePath
87
+ : fromCwd(cwd, modulePath);
88
+
79
89
  const isModuleExcluded = Boolean(
80
- excludeImports.find((pattern) => minimatch(modulePath, pattern))
90
+ excludeImports.find(match(modulePathFromCwd))
81
91
  );
92
+
82
93
  return { modulePath, isModuleExcluded };
83
94
  };
84
95
  };
@@ -131,6 +131,7 @@ module.exports = {
131
131
 
132
132
  const { fileDir, filePath, isExcludedFile } = parseFileSource(
133
133
  options,
134
+ context.cwd,
134
135
  context.filename
135
136
  );
136
137
 
@@ -4,9 +4,8 @@
4
4
  */
5
5
  "use strict";
6
6
 
7
- const { minimatch } = require("minimatch");
8
7
  const helpers = require("../helpers/noDisallowedImports");
9
- const { createAliases } = require("../helpers/common");
8
+ const { createAliases, fromCwd, match } = require("../helpers/common");
10
9
 
11
10
  //------------------------------------------------------------------------------
12
11
  // Rule Definition
@@ -29,6 +28,8 @@ const { createAliases } = require("../helpers/common");
29
28
 
30
29
  const DEFAULT = "default";
31
30
 
31
+ const NAMESPACE = "*";
32
+
32
33
  const importSchema = {
33
34
  type: "object",
34
35
  properties: {
@@ -125,12 +126,11 @@ module.exports = {
125
126
  return [theMember, theImportSpec];
126
127
  }
127
128
 
128
- // const { member, from } = importSpec.import;
129
-
130
129
  const importedSpecifiers = node.specifiers.map((specifier) => {
131
130
  if (specifier.type === "ImportSpecifier")
132
131
  return specifier.imported.name;
133
132
  if (specifier.type === "ImportDefaultSpecifier") return DEFAULT;
133
+ if (specifier.type === "ImportNamespaceSpecifier") return NAMESPACE;
134
134
  return "";
135
135
  });
136
136
 
@@ -138,11 +138,16 @@ module.exports = {
138
138
  importedSpecifiers.some((sp) => sp === specifier)
139
139
  );
140
140
 
141
- if (member && minimatch(modulePath, importSpec.import.from)) {
142
- if (member === DEFAULT) {
141
+ if (
142
+ member &&
143
+ match(fromCwd(context.cwd, modulePath))(importSpec.import.from)
144
+ ) {
145
+ if (member === DEFAULT || member === NAMESPACE) {
143
146
  return [
144
147
  node.specifiers.find(
145
- ({ type }) => type === "ImportDefaultSpecifier"
148
+ ({ type }) =>
149
+ type === "ImportDefaultSpecifier" ||
150
+ type === "ImportNamespaceSpecifier"
146
151
  ).local.name,
147
152
  importSpec,
148
153
  ];
@@ -160,13 +165,10 @@ module.exports = {
160
165
  if (!theMember || !theImportSpec) return;
161
166
 
162
167
  const { allow, disallow } = theImportSpec;
168
+ const fileSourceFromCwd = fromCwd(context.cwd, fileSource);
163
169
  const isAllowed =
164
- (allow
165
- ? Boolean(allow.find((pattern) => minimatch(fileSource, pattern)))
166
- : true) &&
167
- (disallow
168
- ? !disallow.find((pattern) => minimatch(fileSource, pattern))
169
- : true);
170
+ (allow ? Boolean(allow.find(match(fileSourceFromCwd))) : true) &&
171
+ (disallow ? !disallow.find(match(fileSourceFromCwd)) : true);
170
172
  if (isAllowed) return;
171
173
 
172
174
  context.report({
@@ -4,8 +4,8 @@
4
4
  */
5
5
  "use strict";
6
6
 
7
- const { minimatch } = require("minimatch");
8
7
  const path = require("path");
8
+ const { fromCwd, match } = require("../helpers/common");
9
9
 
10
10
  //------------------------------------------------------------------------------
11
11
  // Rule Definition
@@ -95,6 +95,9 @@ module.exports = {
95
95
  },
96
96
  },
97
97
  create(context) {
98
+ /**
99
+ * @type {{include: string[], exclude: string[]}}
100
+ */
98
101
  const options = {
99
102
  exclude: ["**/*.{test,spec}.{js,ts,jsx,tsx}"],
100
103
  include: ["**/*.{js,ts,jsx,tsx}"],
@@ -103,13 +106,12 @@ module.exports = {
103
106
 
104
107
  const fileSource = path.resolve(context.filename);
105
108
 
106
- const isIncludedFile = options.include.find((pattern) =>
107
- minimatch(fileSource, pattern)
108
- );
109
+ const fileSourceFromCwd = fromCwd(context.cwd, fileSource);
110
+
111
+ const isIncludedFile = options.include.find(match(fileSourceFromCwd));
109
112
 
110
113
  const isExcludedFile =
111
- !isIncludedFile ||
112
- options.exclude.find((pattern) => minimatch(fileSource, pattern));
114
+ !isIncludedFile || options.exclude.find(match(fileSourceFromCwd));
113
115
 
114
116
  if (isExcludedFile) return {};
115
117
 
@@ -145,6 +147,7 @@ module.exports = {
145
147
  "ArrowFunctionExpression",
146
148
  "FunctionExpression",
147
149
  "CallExpression",
150
+ "TaggedTemplateExpression",
148
151
  ].includes(token.declarations[0].init.type);
149
152
 
150
153
  if (isFuncDeclaration || isVarDeclaration) {
@@ -74,6 +74,7 @@ module.exports = {
74
74
 
75
75
  const { fileDir, filePath, isExcludedFile } = helper.parseFileSource(
76
76
  options,
77
+ context.cwd,
77
78
  context.filename
78
79
  );
79
80
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-stratified-design",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "ESlint rules for stratified design",
5
5
  "keywords": [
6
6
  "eslint",
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @fileoverview test for helpers/stratified-imports
2
+ * @fileoverview test for helpers/lower-level-imports
3
3
  * @author Hodoug Joung
4
4
  */
5
5
  "use strict";
@@ -17,7 +17,7 @@ const {
17
17
  // Tests
18
18
  //------------------------------------------------------------------------------
19
19
 
20
- describe("helpers/stratified-imports", () => {
20
+ describe("helpers/lower-level-imports", () => {
21
21
  describe("createModulePath()", () => {
22
22
  const cwd = "/proj";
23
23
  const fileDir = "/proj/src/layerA";
@@ -208,6 +208,7 @@ describe("helpers/stratified-imports", () => {
208
208
 
209
209
  describe("parseFileSource()", () => {
210
210
  const makeOptions = (options) => ({ alias: {}, ...options });
211
+ const cwd = "proj";
211
212
  const fileSource = "proj/src/layerA/layerAA.js";
212
213
  const testCases = [
213
214
  {
@@ -246,7 +247,7 @@ describe("helpers/stratified-imports", () => {
246
247
  ];
247
248
  testCases.forEach(({ options, expected }) => {
248
249
  it(`${JSON.stringify(options)} => ${JSON.stringify(expected)}`, () => {
249
- assert.deepEqual(parseFileSource(options, fileSource), expected);
250
+ assert.deepEqual(parseFileSource(options, cwd, fileSource), expected);
250
251
  });
251
252
  });
252
253
  });
@@ -118,7 +118,7 @@ ruleTester.run("lower-level-imports", rule, {
118
118
  structure,
119
119
  root: "./src",
120
120
  include: ["**/*.js"],
121
- exclude: ["**/otherLayerA.test.js"],
121
+ exclude: ["src/otherLayerA.test.js"],
122
122
  },
123
123
  ],
124
124
  },
@@ -37,7 +37,7 @@ ruleTester.run("no-disallowed-imports", rule, {
37
37
  filename: "./src/fileB.js",
38
38
  options: [
39
39
  {
40
- imports: [{ import: { member: ["foo"], from: "**/src/fileA" } }],
40
+ imports: [{ import: { member: ["foo"], from: "src/fileA" } }],
41
41
  },
42
42
  ],
43
43
  },
@@ -48,12 +48,12 @@ ruleTester.run("no-disallowed-imports", rule, {
48
48
  {
49
49
  imports: [
50
50
  {
51
- import: { member: ["baz"], from: "**/src/fileC" },
52
- allow: ["**/src/**/*.js"],
51
+ import: { member: ["baz"], from: "src/fileC" },
52
+ allow: ["src/**/*.js"],
53
53
  },
54
54
  {
55
- import: { member: ["foo", "bar"], from: "**/src/fileA" },
56
- allow: ["**/src/**/*.js"],
55
+ import: { member: ["foo", "bar"], from: "src/fileA" },
56
+ allow: ["src/**/*.js"],
57
57
  },
58
58
  ],
59
59
  },
@@ -66,8 +66,8 @@ ruleTester.run("no-disallowed-imports", rule, {
66
66
  {
67
67
  imports: [
68
68
  {
69
- import: { member: ["foo", "bar"], from: "**/src/fileA" },
70
- disallow: ["**/src/**/*.test.js"],
69
+ import: { member: ["foo", "bar"], from: "src/fileA" },
70
+ disallow: ["src/**/*.test.js"],
71
71
  },
72
72
  ],
73
73
  },
@@ -80,9 +80,9 @@ ruleTester.run("no-disallowed-imports", rule, {
80
80
  {
81
81
  imports: [
82
82
  {
83
- import: { member: ["foo", "bar"], from: "**/src/fileA" },
84
- allow: ["**/src/**/*.js"],
85
- disallow: ["**/src/**/*.test.js"],
83
+ import: { member: ["foo", "bar"], from: "src/fileA" },
84
+ allow: ["src/**/*.js"],
85
+ disallow: ["src/**/*.test.js"],
86
86
  },
87
87
  ],
88
88
  },
@@ -95,8 +95,8 @@ ruleTester.run("no-disallowed-imports", rule, {
95
95
  {
96
96
  imports: [
97
97
  {
98
- import: { member: ["foo"], from: "**/src/fileA" },
99
- allow: ["**/src/**/*.js"],
98
+ import: { member: ["foo"], from: "src/fileA" },
99
+ allow: ["src/**/*.js"],
100
100
  },
101
101
  ],
102
102
  aliases: { "@/": "./src/" },
@@ -110,8 +110,22 @@ ruleTester.run("no-disallowed-imports", rule, {
110
110
  {
111
111
  imports: [
112
112
  {
113
- import: { member: ["default"], from: "**/src/fileA" },
114
- allow: ["**/src/**/*.js"],
113
+ import: { member: ["default"], from: "src/fileA" },
114
+ allow: ["src/**/*.js"],
115
+ },
116
+ ],
117
+ },
118
+ ],
119
+ },
120
+ {
121
+ code: "import * as name from './fileA'",
122
+ filename: "./src/fileB.js",
123
+ options: [
124
+ {
125
+ imports: [
126
+ {
127
+ import: { member: ["*"], from: "src/fileA" },
128
+ allow: ["src/**/*.js"],
115
129
  },
116
130
  ],
117
131
  },
@@ -126,8 +140,8 @@ ruleTester.run("no-disallowed-imports", rule, {
126
140
  {
127
141
  imports: [
128
142
  {
129
- import: { member: ["foo"], from: "**/src/fileA" },
130
- allow: ["**/src/**/*.test.js"],
143
+ import: { member: ["foo"], from: "src/fileA" },
144
+ allow: ["src/**/*.test.js"],
131
145
  },
132
146
  ],
133
147
  },
@@ -141,8 +155,8 @@ ruleTester.run("no-disallowed-imports", rule, {
141
155
  {
142
156
  imports: [
143
157
  {
144
- import: { member: ["foo"], from: "**/src/fileA" },
145
- disallow: ["**/src/**/*.js"],
158
+ import: { member: ["foo"], from: "src/fileA" },
159
+ disallow: ["src/**/*.js"],
146
160
  },
147
161
  ],
148
162
  },
@@ -156,9 +170,9 @@ ruleTester.run("no-disallowed-imports", rule, {
156
170
  {
157
171
  imports: [
158
172
  {
159
- import: { member: ["foo"], from: "**/src/fileA" },
160
- allow: ["**/src/**/*.js"],
161
- disallow: ["**/src/**/fileB.*"],
173
+ import: { member: ["foo"], from: "src/fileA" },
174
+ allow: ["src/**/*.js"],
175
+ disallow: ["src/**/fileB.*"],
162
176
  },
163
177
  ],
164
178
  },
@@ -172,13 +186,30 @@ ruleTester.run("no-disallowed-imports", rule, {
172
186
  {
173
187
  imports: [
174
188
  {
175
- import: { member: ["default"], from: "**/src/fileA" },
176
- disallow: ["**/src/**/*.js"],
189
+ import: { member: ["default"], from: "src/fileA" },
190
+ disallow: ["src/**/*.js"],
177
191
  },
178
192
  ],
179
193
  },
180
194
  ],
181
195
  errors: [{ messageId: "no-disallowed-imports", data: { member: "foo" } }],
182
196
  },
197
+ {
198
+ code: "import * as name from './fileA'",
199
+ filename: "./src/fileB.js",
200
+ options: [
201
+ {
202
+ imports: [
203
+ {
204
+ import: { member: ["*"], from: "src/fileA" },
205
+ disallow: ["src/**/*.js"],
206
+ },
207
+ ],
208
+ },
209
+ ],
210
+ errors: [
211
+ { messageId: "no-disallowed-imports", data: { member: "name" } },
212
+ ],
213
+ },
183
214
  ],
184
215
  });
@@ -48,7 +48,7 @@ ruleTester.run("no-same-level-funcs", rule, {
48
48
  {
49
49
  code: "function func1(){}; function func2(){ func1(); }",
50
50
  filename: "./src/foo.js",
51
- options: [{ include: ["**/src/**/*.*"], exclude: ["**/foo.js"] }],
51
+ options: [{ include: ["src/**/*.*"], exclude: ["src/foo.js"] }],
52
52
  },
53
53
  {
54
54
  code: "// @level 2\nfunction func2(){};\n// @level 1\nfunction func1(){ func2(); }",
@@ -126,6 +126,16 @@ ruleTester.run("no-same-level-funcs", rule, {
126
126
  filename: "./src/foo.js",
127
127
  errors: [{ messageId: "no-same-level-funcs", data: { func: "fn" } }],
128
128
  },
129
+ {
130
+ code: "const fn1 = template``; const fn2 = () => fn1();",
131
+ filename: "./src/foo.js",
132
+ errors: [{ messageId: "no-same-level-funcs", data: { func: "fn1" } }],
133
+ },
134
+ {
135
+ code: "const Comp1 = styled.div``; const Comp2 = () => <Comp1 />",
136
+ filename: "./src/foo.js",
137
+ errors: [{ messageId: "no-same-level-funcs", data: { func: "Comp1" } }],
138
+ },
129
139
  {
130
140
  code: "function CompA() { return <div /> }; function CompB() { return <CompA /> };",
131
141
  filename: "./src/foo.js",
@@ -17,5 +17,9 @@ module.exports = {
17
17
  useLevelNumber: true,
18
18
  },
19
19
  ],
20
+ "stratified-design/no-same-level-funcs": [
21
+ "error",
22
+ { exclude: ["src/layer1/module.js"] },
23
+ ],
20
24
  },
21
25
  };
@@ -9,7 +9,7 @@
9
9
  }
10
10
  },
11
11
  "..": {
12
- "version": "0.3.2",
12
+ "version": "0.9.0",
13
13
  "dev": true,
14
14
  "license": "ISC",
15
15
  "dependencies": {
@@ -1,3 +1,11 @@
1
1
  import { func } from "../layer3/1 lib"; // lint error occurs!!
2
2
 
3
3
  func(1);
4
+
5
+ const fn1 = () => "";
6
+
7
+ const fn2 = () => {
8
+ fn1();
9
+ };
10
+
11
+ export { fn2 };