eslint-plugin-smarthr 0.0.1 → 0.1.2

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/CHANGELOG.md CHANGED
@@ -2,6 +2,27 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [0.1.2](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.1.1...v0.1.2) (2022-03-09)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * require-barrel-import修正(barrelファイルが複数存在する場合、一番親に当たるファイルを検知する) ([#14](https://github.com/kufu/eslint-plugin-smarthr/issues/14)) ([87a6724](https://github.com/kufu/eslint-plugin-smarthr/commit/87a67240f31c9408faad6784741bbf6a2f7ef47b))
11
+
12
+ ### [0.1.1](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.1.0...v0.1.1) (2022-03-08)
13
+
14
+
15
+ ### Features
16
+
17
+ * add require-barrel-import rule ([#13](https://github.com/kufu/eslint-plugin-smarthr/issues/13)) ([79ee88d](https://github.com/kufu/eslint-plugin-smarthr/commit/79ee88d355e01bb8344dc95bd65157e2fbcf916e))
18
+
19
+ ## [0.1.0](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.0.1...v0.1.0) (2022-02-09)
20
+
21
+
22
+ ### ⚠ BREAKING CHANGES
23
+
24
+ * BREAKING CHANGE: add require-import & update prohibit-import (#12) ([e6c5c44](https://github.com/kufu/eslint-plugin-smarthr/commit/e6c5c445a21620d4b796ded00a685e5da367c7bb)), closes [#12](https://github.com/kufu/eslint-plugin-smarthr/issues/12)
25
+
5
26
  ### [0.0.1](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.0.0...v0.0.1) (2022-02-08)
6
27
 
7
28
 
@@ -10,9 +31,6 @@ All notable changes to this project will be documented in this file. See [standa
10
31
  * add type property function params redundant ([758df90](https://github.com/kufu/eslint-plugin-smarthr/commit/758df90f89bd27dd589aeeb55165e27c8e072b08))
11
32
  * redundant-name の修正候補を操作できるように改修 ([20991e8](https://github.com/kufu/eslint-plugin-smarthr/commit/20991e874890556e84e7c682e789e4b2650a85b0))
12
33
 
13
- ### [0.0.2](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.0.1...v0.0.2) (2022-01-26)
14
-
15
- ### [0.0.1](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.0.0...v0.0.1) (2022-01-26)
16
34
 
17
35
  ## 0.0.0 (2022-01-25)
18
36
 
package/README.md CHANGED
@@ -145,13 +145,19 @@ import globalModulePart from '@/modules/views/parts'
145
145
  ## smarthr/jsx-start-with-spread-attributes
146
146
 
147
147
  - jsxを記述する際、意図しない属性の上書きを防ぐため、spread-attributesを先に指定するように強制するruleです
148
+ - eslint を `--fix` オプション付きで実行する際、 fix option を true にすると自動修正します
148
149
 
149
150
  ### rules
150
151
 
151
152
  ```js
152
153
  {
153
154
  rules: {
154
- 'smarthr/jsx-start-with-spread-attributes': 'error', // 'warn', 'off'
155
+ 'smarthr/jsx-start-with-spread-attributes': [
156
+ 'error', // 'warn', 'off'
157
+ {
158
+ fix: false, // true
159
+ },
160
+ ]
155
161
  },
156
162
  }
157
163
  ```
@@ -268,12 +274,17 @@ import globalModulePart from '@/modules/views/parts'
268
274
  'smarthr/prohibit-import': [
269
275
  'error', // 'warn', 'off'
270
276
  {
271
- targets: {
272
- '^query-string$': true, // key は 正規表現を指定する
273
- '^smarthr-ui$': ['SecondaryButtonAnchor'],
277
+ '^.+$': {
278
+ 'smarthr-ui': {
279
+ imported: ['SecondaryButtonAnchor'],
280
+ reportMessage: `{{module}}/{{export}} はXxxxxxなので利用せず yyyy/zzzz を利用してください`
281
+ },
282
+ }
283
+ '\/pages\/views\/': {
284
+ 'query-string': {
285
+ imported: true,
286
+ },
274
287
  },
275
- // generateReportMessage: (source, imported) =>
276
- // `${source}${imported && `/${imported}`} はXxxxxxなので利用せず yyyy/zzzz を利用してください`
277
288
  }
278
289
  ]
279
290
  },
@@ -283,6 +294,7 @@ import globalModulePart from '@/modules/views/parts'
283
294
  ### ❌ Incorrect
284
295
 
285
296
  ```js
297
+ // src/pages/views/Page.tsx
286
298
  import queryString from 'query-string'
287
299
  import { SecondaryButtonAnchor } from 'smarthr-ui'
288
300
  ```
@@ -291,10 +303,101 @@ import { SecondaryButtonAnchor } from 'smarthr-ui'
291
303
 
292
304
 
293
305
  ```js
306
+ // src/pages/views/Page.tsx
294
307
  import { PrimaryButton, SecondaryButton } from 'smarthr-ui'
295
308
  ```
296
309
 
310
+ ## smarthr/require-import
311
+
312
+ - 対象ファイルにimportを強制させたい場合に利用します
313
+ - 例: Page.tsx ではページタイトルを設定させたいので useTitle を必ずimportさせたい
314
+
315
+ ### rules
316
+
317
+ ```js
318
+ {
319
+ rules: {
320
+ 'smarthr/require-import': [
321
+ 'error',
322
+ {
323
+ 'Buttons\/.+\.tsx': {
324
+ 'smarthr-ui': {
325
+ imported: ['SecondaryButton'],
326
+ reportMessage: 'Buttons以下のコンポーネントでは {{module}}/{{export}} を拡張するようにしてください',
327
+ },
328
+ },
329
+ 'Page.tsx$': {
330
+ './client/src/hooks/useTitle': {
331
+ imported: true,
332
+ reportMessage: '{{module}} を利用してください(ページタイトルを設定するため必要です)',
333
+ },
334
+ },
335
+ },
336
+ ]
337
+ },
338
+ }
339
+ ```
340
+
341
+ ### ❌ Incorrect
342
+
343
+ ```js
344
+ // client/src/Buttons/SecondaryButton.tsx
345
+ import { SecondaryButtonAnchor } from 'smarthr-ui'
346
+
347
+ // client/src/Page.tsx
348
+ import { SecondaryButton } from 'smarthr-ui'
349
+ ```
350
+
351
+ ### ✅ Correct
352
+
297
353
 
354
+ ```js
355
+ // client/src/Buttons/SecondaryButton.tsx
356
+ import { SecondaryButton } from 'smarthr-ui'
357
+
358
+ // client/src/Page.tsx
359
+ import useTitle from '.hooks/useTitle'
360
+ ```
361
+
362
+ ## smarthr/require-barrel-import
363
+
364
+ - tsconfig.json の compilerOptions.pathsに '@/*' としてroot path を指定する必要があります
365
+ - importした対象が本来exportされているべきであるbarrel(index.tsなど)が有る場合、import pathの変更を促します
366
+ - 例: Page/parts/Menu/Item の import は Page/parts/Menu から行わせたい
367
+ - ディレクトリ内のindexファイルを捜査し、対象を決定します
368
+
369
+ ### rules
370
+
371
+ ```js
372
+ {
373
+ rules: {
374
+ 'smarthr/require-barrel-import': 'error',
375
+ },
376
+ }
377
+ ```
378
+
379
+ ### ❌ Incorrect
380
+
381
+ ```js
382
+ // client/src/views/Page/parts/Menu/index.ts
383
+ export { Menu } from './Menu'
384
+ export { Item } from './Item'
385
+
386
+ // client/src/App.tsx
387
+ import { Item } from './Page/parts/Menu/Item'
388
+ ```
389
+
390
+ ### ✅ Correct
391
+
392
+
393
+ ```js
394
+ // client/src/views/Page/parts/Menu/index.ts
395
+ export { Menu } from './Menu'
396
+ export { Item } from './Item'
397
+
398
+ // client/src/App.tsx
399
+ import { Item } from './Page/parts/Menu'
400
+ ```
298
401
 
299
402
 
300
403
  ## smarthr/redundant-name
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-smarthr",
3
- "version": "0.0.1",
3
+ "version": "0.1.2",
4
4
  "author": "SmartHR",
5
5
  "license": "MIT",
6
6
  "description": "A sharable ESLint plugin for SmartHR",
@@ -1,38 +1,72 @@
1
+ const SCHEMA = [
2
+ {
3
+ type: 'object',
4
+ properties: {
5
+ fix: { type: 'boolean', default: false },
6
+ },
7
+ additionalProperties: false,
8
+ }
9
+ ]
10
+
1
11
  module.exports = {
2
12
  meta: {
3
13
  type: 'suggestion',
4
14
  messages: {
5
15
  'jsx-start-with-spread-attributes': '{{ message }}',
6
16
  },
7
- schema: [],
17
+ fixable: 'code',
18
+ schema: SCHEMA,
8
19
  },
9
20
  create(context) {
10
21
  return {
11
22
  JSXSpreadAttribute: (node) => {
12
- // HINT: 0: 計算中 1: 見つからなかった 2: 見つかった
13
- const hit = node.parent.attributes.reduce((h, a) => {
14
- if (h === 0) {
23
+ // HINT: -2: 計算中 -1: 見つからなかった >= 0: 見つかった
24
+ const insertIndex = node.parent.attributes.reduce((h, a, i) => {
25
+ if (h === -2) {
15
26
  if (a === node) {
16
- return 1
27
+ return -1
17
28
  }
18
29
 
19
- return a.type !== 'JSXSpreadAttribute' ? 2 : 0
30
+ return a.type !== 'JSXSpreadAttribute' ? i : h
20
31
  }
21
32
 
22
33
  return h
23
- }, 0)
34
+ }, -2)
35
+
36
+ if (insertIndex >= 0) {
37
+ const option = context.options[0]
38
+ const sourceCode = context.getSourceCode()
39
+ const attributeCode = sourceCode.getText(node)
24
40
 
25
- if (hit === 2) {
26
41
  context.report({
27
42
  node,
28
43
  messageId: 'jsx-start-with-spread-attributes',
29
44
  data: {
30
- message: `"${context.getSourceCode().getText(node)}" は他の属性より先に記述してください`,
45
+ message: `"${attributeCode}" は意図しない上書きを防ぐため、spread attributesでない属性より先に記述してください`,
31
46
  },
47
+ fix: option?.fix ? (fixer) => {
48
+ const elementNode = node.parent
49
+ const sortedAttributes = [...elementNode.attributes].reduce((p, a, i) => {
50
+ if (insertIndex === i) {
51
+ p = [attributeCode, ...p]
52
+ }
53
+
54
+ if (a !== node) {
55
+ p = [...p, sourceCode.getText(a)]
56
+ }
57
+
58
+ return p
59
+ }, [])
60
+
61
+ return fixer.replaceText(
62
+ elementNode,
63
+ `<${elementNode.name.name} ${sortedAttributes.join(' ')}${elementNode.selfClosing ? '/' : ''}>`
64
+ )
65
+ } : null
32
66
  });
33
67
  }
34
68
  },
35
69
  }
36
70
  },
37
71
  }
38
- module.exports.schema = []
72
+ module.exports.schema = SCHEMA
@@ -1,28 +1,34 @@
1
+ const path = require('path')
2
+
1
3
  const SCHEMA = [{
2
- type: "object",
4
+ type: 'object',
3
5
  patternProperties: {
4
- ".+": {
5
- type: "object",
6
- required: [
7
- "imported",
8
- ],
9
- properties: {
10
- imported: {
11
- type: ["boolean", "array"],
12
- items: {
13
- type: "string"
14
- }
15
- },
16
- reportMessage: {
17
- type: "string"
6
+ '.+': {
7
+ type: 'object',
8
+ patternProperties: {
9
+ '.+': {
10
+ type: 'object',
11
+ required: [
12
+ 'imported',
13
+ ],
14
+ properties: {
15
+ imported: {
16
+ type: ['boolean', 'array'],
17
+ items: {
18
+ type: 'string',
19
+ },
20
+ },
21
+ reportMessage: {
22
+ type: 'string',
23
+ },
24
+ },
25
+ additionalProperties: false
18
26
  }
19
- },
20
- additionalProperties: false
21
27
  }
22
28
  },
23
- additionalProperties: true,
24
- }
25
- ]
29
+ },
30
+ additionalProperties: true,
31
+ }]
26
32
 
27
33
  const defaultReportMessage = (moduleName, exportName) => `${moduleName}${typeof exportName == 'string' ? `/${exportName}`: ''} は利用しないでください`
28
34
 
@@ -36,34 +42,59 @@ module.exports = {
36
42
  },
37
43
  create(context) {
38
44
  const options = context.options[0]
39
- const targetModules = Object.keys(options)
45
+ const filename = context.getFilename()
46
+ const parentDir = (() => {
47
+ const dir = filename.match(/^(.+?)\..+?$/)[1].split('/')
48
+ dir.pop()
49
+
50
+ return dir.join('/')
51
+ })()
52
+ const targetPathRegexs = Object.keys(options)
53
+ const targetProhibits = targetPathRegexs.filter((regex) => !!filename.match(new RegExp(regex)))
54
+
55
+ if (targetProhibits.length === 0) {
56
+ return {}
57
+ }
58
+
40
59
  return {
41
60
  ImportDeclaration: (node) => {
42
- targetModules.forEach((targetModule) => {
43
- if (!node.source.value.match(new RegExp(targetModule))) {
44
- return
45
- }
46
-
47
- const {imported, reportMessage} = Object.assign({imported: true}, options[targetModule])
48
- const useImported = (() => {
49
- if (!Array.isArray(imported)) {
50
- return !!imported
61
+ targetProhibits.forEach((prohibitKey) => {
62
+ const option = options[prohibitKey]
63
+ const targetModules = Object.keys(option)
64
+
65
+ targetModules.forEach((targetModule) => {
66
+ const { imported, reportMessage } = Object.assign({imported: true}, option[targetModule])
67
+ const actualTarget = targetModule[0] !== '.' ? targetModule : path.resolve(`${process.cwd()}/${targetModule}`)
68
+ let sourceValue = node.source.value
69
+
70
+ if (actualTarget[0] === '/') {
71
+ sourceValue = path.resolve(`${parentDir}/${sourceValue}`)
51
72
  }
52
73
 
53
- const specifier = node.specifiers.find((s) => s.imported && imported.includes(s.imported.name))
74
+ if (actualTarget !== sourceValue) {
75
+ return
76
+ }
77
+
78
+ const useImported = (() => {
79
+ if (!Array.isArray(imported)) {
80
+ return !!imported
81
+ }
54
82
 
55
- return specifier ? specifier.imported.name : false
56
- })()
83
+ const specifier = node.specifiers.find((s) => s.imported && imported.includes(s.imported.name))
57
84
 
58
- if (useImported) {
59
- context.report({
60
- node,
61
- messageId: 'prohibit_import',
62
- data: {
63
- message: reportMessage ? reportMessage.replace('{{module}}', node.source.value).replace('{{export}}', useImported) : defaultReportMessage(node.source.value, useImported)
64
- },
65
- });
66
- }
85
+ return specifier ? specifier.imported.name : false
86
+ })()
87
+
88
+ if (useImported) {
89
+ context.report({
90
+ node,
91
+ messageId: 'prohibit_import',
92
+ data: {
93
+ message: reportMessage ? reportMessage.replace('{{module}}', node.source.value).replace('{{export}}', useImported) : defaultReportMessage(node.source.value, useImported)
94
+ },
95
+ });
96
+ }
97
+ })
67
98
  })
68
99
  },
69
100
  }
@@ -0,0 +1,122 @@
1
+ const path = require('path')
2
+ const fs = require('fs')
3
+ const { replacePaths, rootPath } = require('../libs/common')
4
+ const calculateAbsoluteImportPath = (source) => {
5
+ if (source[0] === '/') {
6
+ return source
7
+ }
8
+
9
+ return Object.entries(replacePaths).reduce((prev, [key, values]) => {
10
+ if (source === prev) {
11
+ return values.reduce((p, v) => {
12
+ if (prev === p) {
13
+ const regexp = new RegExp(`^${key}(.+)$`)
14
+
15
+ if (prev.match(regexp)) {
16
+ return p.replace(regexp, `${path.resolve(`${process.cwd()}/${v}`)}/$1`)
17
+ }
18
+ }
19
+
20
+ return p
21
+ }, prev)
22
+ }
23
+
24
+ return prev
25
+ }, source)
26
+ }
27
+ const calculateReplacedImportPath = (source) => {
28
+ return Object.entries(replacePaths).reduce((prev, [key, values]) => {
29
+ if (source === prev) {
30
+ return values.reduce((p, v) => {
31
+ if (prev === p) {
32
+ const regexp = new RegExp(`^${path.resolve(`${process.cwd()}/${v}`)}(.+)$`)
33
+
34
+ if (prev.match(regexp)) {
35
+ return p.replace(regexp, `${key}/$1`).replace(/(\/)+/g, '/')
36
+ }
37
+ }
38
+
39
+ return p
40
+ }, prev)
41
+ }
42
+
43
+ return prev
44
+ }, source)
45
+ }
46
+ const TARGET_EXTS = ['ts', 'tsx', 'js', 'jsx']
47
+
48
+ module.exports = {
49
+ meta: {
50
+ type: 'suggestion',
51
+ messages: {
52
+ 'require-barrel-import': '{{ message }}',
53
+ },
54
+ schema: [],
55
+ },
56
+ create(context) {
57
+ const filename = context.getFilename()
58
+
59
+ const dir = (() => {
60
+ const d = filename.split('/')
61
+ d.pop()
62
+
63
+ return d.join('/')
64
+ })()
65
+
66
+ return {
67
+ ImportDeclaration: (node) => {
68
+ let sourceValue = node.source.value
69
+
70
+ if (sourceValue[0] === '.') {
71
+ sourceValue = path.resolve(`${dir}/${sourceValue}`)
72
+ }
73
+
74
+ sourceValue = calculateAbsoluteImportPath(sourceValue)
75
+
76
+ if (sourceValue[0] !== '/') {
77
+ return
78
+ }
79
+
80
+ const sources = sourceValue.split('/')
81
+
82
+ // HINT: directoryの場合、indexファイルからimportしていることは自明であるため、一階層上からチェックする
83
+ if (fs.existsSync(sourceValue) && fs.statSync(sourceValue).isDirectory()) {
84
+ sources.pop()
85
+ sourceValue = sources.join('/')
86
+ }
87
+
88
+ let barrel = undefined
89
+
90
+ while (sources.length > 0) {
91
+ // HINT: 以下の場合は即終了
92
+ // - import元以下のimportだった場合
93
+ // - rootまで捜索した場合
94
+ if (
95
+ dir === rootPath ||
96
+ dir.match(new RegExp(`^${sourceValue}`))
97
+ ) {
98
+ break
99
+ }
100
+
101
+ barrel = TARGET_EXTS.map((e) => `${sourceValue}/index.${e}`).find((p) => fs.existsSync(p)) || barrel
102
+
103
+ sources.pop()
104
+ sourceValue = sources.join('/')
105
+ }
106
+
107
+ if (barrel) {
108
+ barrel = calculateReplacedImportPath(barrel)
109
+
110
+ context.report({
111
+ node,
112
+ messageId: 'require-barrel-import',
113
+ data: {
114
+ message: `${barrel.replace(/\/index\.(ts|js)x?$/, '')} からimportするか、${barrel} を削除してください`,
115
+ },
116
+ });
117
+ }
118
+ },
119
+ }
120
+ },
121
+ }
122
+ module.exports.schema = []
@@ -0,0 +1,113 @@
1
+ const path = require('path')
2
+ const SCHEMA = [{
3
+ type: 'object',
4
+ patternProperties: {
5
+ '.+': {
6
+ type: 'object',
7
+ patternProperties: {
8
+ '.+': {
9
+ type: 'object',
10
+ required: [
11
+ 'imported',
12
+ ],
13
+ properties: {
14
+ imported: {
15
+ type: ['boolean', 'array'],
16
+ items: {
17
+ type: 'string',
18
+ }
19
+ },
20
+ reportMessage: {
21
+ type: 'string',
22
+ },
23
+ },
24
+ additionalProperties: false,
25
+ }
26
+ },
27
+ additionalProperties: true,
28
+ },
29
+ },
30
+ additionalProperties: true,
31
+ }]
32
+
33
+ const defaultReportMessage = (moduleName, exportName) => `${moduleName}${typeof exportName == 'string' ? `/${exportName}`: ''} をimportしてください`
34
+
35
+ module.exports = {
36
+ meta: {
37
+ type: 'suggestion',
38
+ messages: {
39
+ 'require_import': '{{ message }}',
40
+ },
41
+ schema: SCHEMA,
42
+ },
43
+ create(context) {
44
+ const options = context.options[0]
45
+ const filename = context.getFilename()
46
+ const targetPathRegexs = Object.keys(options)
47
+ const targetRequires = targetPathRegexs.filter((regex) => !!filename.match(new RegExp(regex)))
48
+
49
+ if (targetRequires.length === 0) {
50
+ return {}
51
+ }
52
+
53
+ return {
54
+ Program: (node) => {
55
+ const importDeclarations = node.body.filter((item) => item.type === 'ImportDeclaration')
56
+ const parentDir = (() => {
57
+ const dir = filename.match(/^(.+?)\..+?$/)[1].split('/')
58
+ dir.pop()
59
+
60
+ return dir.join('/')
61
+ })()
62
+
63
+ targetRequires.forEach((requireKey) => {
64
+ const option = options[requireKey]
65
+
66
+ Object.keys(option).forEach((targetModule) => {
67
+ const { imported, reportMessage, targetRegex } = Object.assign({imported: true}, option[targetModule])
68
+
69
+ if (targetRegex && !filename.match(new RegExp(targetRegex))) {
70
+ return
71
+ }
72
+
73
+ const actualTarget = targetModule[0] !== '.' ? targetModule : path.resolve(`${process.cwd()}/${targetModule}`)
74
+ const importDeclaration = importDeclarations.find(
75
+ actualTarget[0] !== '/' ? (
76
+ (id) => id.source.value === actualTarget
77
+ ) : (
78
+ (id) => path.resolve(`${parentDir}/${id.source.value}`) === actualTarget
79
+ )
80
+ )
81
+ const reporter = (item) => {
82
+ context.report({
83
+ node,
84
+ messageId: 'require_import',
85
+ data: {
86
+ message: reportMessage ? reportMessage.replace('{{module}}', actualTarget).replace('{{export}}', item) : defaultReportMessage(actualTarget, item)
87
+ },
88
+ })
89
+ }
90
+
91
+ if (!importDeclaration) {
92
+ if (Array.isArray(imported)) {
93
+ imported.forEach((i) => {
94
+ reporter(i)
95
+ })
96
+ } else if (imported) {
97
+ reporter()
98
+ }
99
+ } else if (Array.isArray(imported)) {
100
+ imported.forEach((i) => {
101
+ if (!importDeclaration.specifiers.find((s) => s.imported && s.imported.name === i)) {
102
+ reporter(i)
103
+ }
104
+ })
105
+ }
106
+ })
107
+ })
108
+ },
109
+ }
110
+ },
111
+ }
112
+
113
+ module.exports.schema = SCHEMA
@@ -1,63 +1,104 @@
1
- const rule = require("../rules/prohibit-import")
2
- const RuleTester = require("eslint").RuleTester
1
+ const rule = require('../rules/prohibit-import')
2
+ const RuleTester = require('eslint').RuleTester
3
3
 
4
4
  const ruleTester = new RuleTester({
5
5
  parserOptions: {
6
- sourceType: "module",
6
+ sourceType: 'module',
7
7
  ecmaVersion: 2015
8
8
  },
9
9
  })
10
10
 
11
- ruleTester.run("prohibit-import-lodash", rule, {
11
+ ruleTester.run('prohibit-import', rule, {
12
12
  valid: [
13
13
  {
14
14
  code: `import _ from 'lodash-es'`,
15
+ filename: 'hoge.js',
15
16
  options: [
16
17
  {
17
- '^lodash$': {
18
- imported: true,
18
+ '^.+$': {
19
+ 'lodash': {
20
+ imported: true,
21
+ },
19
22
  },
20
23
  }
21
24
  ]
22
25
  },
23
26
  {
24
27
  code: `import { isEqual } from 'lodash-es'`,
28
+ filename: 'hoge.js',
25
29
  options: [
26
30
  {
27
- '^lodash$': {
28
- imported: ['isEqual']
31
+ '^.+$': {
32
+ 'lodash': {
33
+ imported: ['isEqual']
34
+ },
29
35
  },
30
36
  }
31
37
  ]
32
38
  },
33
39
  {
34
40
  code: `import { isEqaul } from 'lodash'`,
41
+ filename: 'hoge.js',
35
42
  options: [
36
43
  {
37
- '^lodash$': {
38
- imported: ['isEqual']
44
+ '^.+$': {
45
+ 'lodash': {
46
+ imported: ['isEqual']
47
+ },
39
48
  },
40
49
  }
41
50
  ]
42
51
  },
43
52
  {
44
53
  code: `import _ from 'lodash'`,
54
+ filename: 'hoge.js',
45
55
  options: [
46
56
  {
47
- '^lodash$': {
48
- imported: ['isEqual']
57
+ '^.+$': {
58
+ 'lodash': {
59
+ imported: ['isEqual']
60
+ },
49
61
  },
50
62
  }
51
63
  ]
52
- }
64
+ },
65
+ {
66
+ code: `import _ from 'lodash'`,
67
+ filename: 'hoge.js',
68
+ options: [
69
+ {
70
+ '^fuga.js$': {
71
+ 'lodash': {
72
+ imported: true
73
+ },
74
+ },
75
+ }
76
+ ],
77
+ },
78
+ {
79
+ code: `import { isEqual } from './module/validator'`,
80
+ filename: 'page/hoge.js',
81
+ options: [
82
+ {
83
+ '^.+$': {
84
+ './module/validator': {
85
+ imported: ['isEqual'],
86
+ },
87
+ },
88
+ }
89
+ ],
90
+ },
53
91
  ],
54
92
  invalid: [
55
93
  {
56
94
  code: `import _ from 'lodash'`,
95
+ filename: 'hoge.js',
57
96
  options: [
58
97
  {
59
- '^lodash$': {
60
- imported: true
98
+ '^.+$': {
99
+ 'lodash': {
100
+ imported: true
101
+ },
61
102
  },
62
103
  }
63
104
  ],
@@ -65,10 +106,13 @@ ruleTester.run("prohibit-import-lodash", rule, {
65
106
  },
66
107
  {
67
108
  code: `import { isEqual } from 'lodash'`,
109
+ filename: 'hoge.js',
68
110
  options: [
69
111
  {
70
- '^lodash$': {
71
- imported: true
112
+ '^.+$': {
113
+ 'lodash': {
114
+ imported: true
115
+ },
72
116
  },
73
117
  }
74
118
  ],
@@ -76,10 +120,13 @@ ruleTester.run("prohibit-import-lodash", rule, {
76
120
  },
77
121
  {
78
122
  code: `import { isEqual } from 'lodash'`,
123
+ filename: 'hoge.js',
79
124
  options: [
80
125
  {
81
- '^lodash$': {
82
- imported: ['isEqual']
126
+ '^.+$': {
127
+ 'lodash': {
128
+ imported: ['isEqual']
129
+ },
83
130
  },
84
131
  }
85
132
  ],
@@ -87,11 +134,14 @@ ruleTester.run("prohibit-import-lodash", rule, {
87
134
  },
88
135
  {
89
136
  code: `import { isEqual } from 'lodash'`,
137
+ filename: 'hoge.js',
90
138
  options: [
91
139
  {
92
- '^lodash$': {
93
- imported: ['isEqual'],
94
- "reportMessage": "must not use {{module}}/{{export}}"
140
+ '^.+$': {
141
+ 'lodash': {
142
+ imported: ['isEqual'],
143
+ "reportMessage": "must not use {{module}}/{{export}}"
144
+ },
95
145
  },
96
146
  }
97
147
  ],
@@ -99,18 +149,53 @@ ruleTester.run("prohibit-import-lodash", rule, {
99
149
  },
100
150
  {
101
151
  code: `import { isEqual } from 'lodash'`,
152
+ filename: 'hoge.js',
102
153
  options: [
103
154
  {
104
- 'example': {
105
- imported: true,
155
+ '^.+$': {
156
+ 'example': {
157
+ imported: true,
158
+ },
159
+ 'lodash': {
160
+ imported: ['isEqual'],
161
+ reportMessage: "must not use {{module}}/{{export}}",
162
+ },
106
163
  },
107
- '^lodash$': {
108
- imported: ['isEqual'],
109
- reportMessage: "must not use {{module}}/{{export}}",
164
+ }
165
+ ],
166
+ errors: [{message: 'must not use lodash/isEqual'}]
167
+ },
168
+ {
169
+ code: `import { isEqual } from 'lodash'`,
170
+ filename: 'hoge.js',
171
+ options: [
172
+ {
173
+ '^hoge.js$': {
174
+ 'example': {
175
+ imported: true,
176
+ },
177
+ 'lodash': {
178
+ imported: ['isEqual'],
179
+ reportMessage: "must not use {{module}}/{{export}}",
180
+ },
110
181
  },
111
182
  }
112
183
  ],
113
184
  errors: [{message: 'must not use lodash/isEqual'}]
114
- }
185
+ },
186
+ {
187
+ code: `import { isEqual } from './module/validator'`,
188
+ filename: 'page/hoge.js',
189
+ options: [
190
+ {
191
+ '^.+$': {
192
+ './page/module/validator': {
193
+ imported: ['isEqual'],
194
+ },
195
+ },
196
+ }
197
+ ],
198
+ errors: [{ message: './module/validator/isEqual は利用しないでください' }]
199
+ },
115
200
  ]
116
- })
201
+ })
@@ -0,0 +1,184 @@
1
+ const rule = require('../rules/require-import')
2
+ const RuleTester = require('eslint').RuleTester
3
+
4
+ const ruleTester = new RuleTester({
5
+ parserOptions: {
6
+ sourceType: 'module',
7
+ ecmaVersion: 2015
8
+ },
9
+ })
10
+
11
+ ruleTester.run('require-import', rule, {
12
+ valid: [
13
+ {
14
+ code: `import _ from 'lodash'`,
15
+ filename: 'hoge.js',
16
+ options: [
17
+ {
18
+ '^.+$': {
19
+ 'lodash': {
20
+ imported: true,
21
+ },
22
+ },
23
+ }
24
+ ],
25
+ },
26
+ {
27
+ code: ``,
28
+ filename: 'hoge.js',
29
+ options: [
30
+ {
31
+ '^fuga.js$': {
32
+ 'lodash': {
33
+ imported: true,
34
+ },
35
+ },
36
+ }
37
+ ],
38
+ },
39
+ {
40
+ code: `import _ from 'lodash'`,
41
+ filename: 'hoge.js',
42
+ options: [
43
+ {
44
+ '^hoge.js$': {
45
+ 'lodash': {
46
+ imported: true,
47
+ reportMessage: '{{module}} を絶対使ってください'
48
+ },
49
+ },
50
+ }
51
+ ],
52
+ },
53
+ {
54
+ code: `import { isEqual } from 'lodash'`,
55
+ filename: 'hoge.js',
56
+ options: [
57
+ {
58
+ '^hoge.js$': {
59
+ 'lodash': {
60
+ imported: ['isEqual'],
61
+ reportMessage: '{{module}}/{{export}} を絶対使ってください'
62
+ },
63
+ },
64
+ }
65
+ ],
66
+ errors: [{ message: 'lodash/isEqual を絶対使ってください' }],
67
+ },
68
+ {
69
+ code: `import { chunk } from 'lodash'`,
70
+ filename: 'hoge.js',
71
+ options: [
72
+ {
73
+ '^hoge.js$': {
74
+ 'lodash': {
75
+ imported: true,
76
+ },
77
+ },
78
+ }
79
+ ],
80
+ },
81
+ {
82
+ code: `import { isEqual } from './module/validator'`,
83
+ filename: 'page/hoge.js',
84
+ options: [
85
+ {
86
+ '^.+$': {
87
+ './page/module/validator': {
88
+ imported: ['isEqual'],
89
+ },
90
+ },
91
+ }
92
+ ],
93
+ },
94
+ ],
95
+ invalid: [
96
+ {
97
+ code: ``,
98
+ filename: 'hoge.js',
99
+ options: [
100
+ {
101
+ '^.+$': {
102
+ 'lodash': {
103
+ imported: true,
104
+ },
105
+ },
106
+ }
107
+ ],
108
+ errors: [{ message: 'lodash をimportしてください' }],
109
+ },
110
+ {
111
+ code: ``,
112
+ filename: 'hoge.js',
113
+ options: [
114
+ {
115
+ '^hoge.js$': {
116
+ 'lodash': {
117
+ imported: true,
118
+ },
119
+ },
120
+ }
121
+ ],
122
+ errors: [{ message: 'lodash をimportしてください' }],
123
+ },
124
+ {
125
+ code: ``,
126
+ filename: 'hoge.js',
127
+ options: [
128
+ {
129
+ '^hoge.js$': {
130
+ 'lodash': {
131
+ imported: true,
132
+ reportMessage: '{{module}} を絶対使ってください'
133
+ },
134
+ },
135
+ }
136
+ ],
137
+ errors: [{ message: 'lodash を絶対使ってください' }],
138
+ },
139
+ {
140
+ code: ``,
141
+ filename: 'hoge.js',
142
+ options: [
143
+ {
144
+ '^hoge.js$': {
145
+ 'lodash': {
146
+ imported: ['isEqual'],
147
+ reportMessage: '{{module}}/{{export}} を絶対使ってください'
148
+ },
149
+ },
150
+ }
151
+ ],
152
+ errors: [{ message: 'lodash/isEqual を絶対使ってください' }],
153
+ },
154
+ {
155
+ code: `import { chunk } from 'lodash'`,
156
+ filename: 'hoge.js',
157
+ options: [
158
+ {
159
+ '^hoge.js$': {
160
+ 'lodash': {
161
+ imported: ['isEqual'],
162
+ reportMessage: '{{module}}/{{export}} を絶対使ってください'
163
+ },
164
+ },
165
+ }
166
+ ],
167
+ errors: [{ message: 'lodash/isEqual を絶対使ってください' }],
168
+ },
169
+ {
170
+ code: `import { isEqual } from './module/validator'`,
171
+ filename: 'page/hoge.js',
172
+ options: [
173
+ {
174
+ '^.+$': {
175
+ './module/validator': {
176
+ imported: ['isEqual'],
177
+ },
178
+ },
179
+ }
180
+ ],
181
+ errors: [{ message: /module\/validator\/isEqual をimportしてください$/ }],
182
+ },
183
+ ]
184
+ })