@foundbyte/eslint-plugin 1.1.5-alpha.0 → 1.1.5-alpha.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/README.md CHANGED
@@ -77,6 +77,13 @@ module.exports = {
77
77
  },
78
78
  ],
79
79
  '@foundbyte/require-json-parse-type': 'error',
80
+ '@foundbyte/no-restricted-literal-types': [
81
+ 'error',
82
+ {
83
+ forbidden: ['null', 'undefined', ''],
84
+ allowInUnions: true,
85
+ },
86
+ ],
80
87
  },
81
88
  }
82
89
  ```
@@ -631,9 +638,15 @@ const a: { s: number } = JSON.parse('{s:1}')
631
638
  // ✅ 正确 - 解构赋值带有显式类型注解
632
639
  const { s }: { s: number } = JSON.parse('{s:1}')
633
640
 
641
+ // ✅ 正确 - 三元表达式中的 JSON.parse 带有显式类型注解
642
+ const qualification: string[] = info.qualification ? JSON.parse(info.qualification) : []
643
+
634
644
  // ❌ 错误 - 缺少类型注解
635
645
  const a = JSON.parse('{s:1}')
636
646
 
647
+ // ❌ 错误 - 三元表达式中的 JSON.parse 缺少类型注解
648
+ const qualification = info.qualification ? JSON.parse(info.qualification) : []
649
+
637
650
  // ❌ 错误 - 使用 as 断言不算作变量声明类型注解
638
651
  const a = JSON.parse('{s:1}') as { s: number }
639
652
  ```
@@ -646,6 +659,73 @@ const a = JSON.parse('{s:1}') as { s: number }
646
659
 
647
660
  **注意:**
648
661
 
649
- - 该规则只检查变量声明的初始化表达式直接是 `JSON.parse(...)` 的情况
662
+ - 该规则检查变量声明的初始化表达式中是否包含 `JSON.parse(...)`,包括三元表达式、逻辑表达式、对象/数组字面量等场景
650
663
  - 对于 `const a = JSON.parse('...') as Type` 也会报错,因为它要求类型注解必须声明在变量本身
664
+ - 函数体内部的 `JSON.parse` 不影响外层变量,不会被误判
651
665
  - 如希望允许 `as` 类型断言,请搭配 `@foundbyte/no-initial-as-assertion` 规则按需配置
666
+
667
+ ### no-restricted-literal-types
668
+
669
+ 禁止在类型注解中使用指定的无意义字面量类型,例如 `null`、`undefined`、空字符串字面量 `''` 等。
670
+
671
+ **配置示例:**
672
+
673
+ ```javascript
674
+ // 基本用法 - 禁止 null、undefined 和空字符串字面量类型
675
+ '@foundbyte/no-restricted-literal-types': ['error', {
676
+ forbidden: ['null', 'undefined', ''],
677
+ }]
678
+
679
+ // 允许联合类型中的 null / undefined
680
+ '@foundbyte/no-restricted-literal-types': ['error', {
681
+ forbidden: ['null', 'undefined'],
682
+ allowInUnions: true,
683
+ }]
684
+ ```
685
+
686
+ **示例:**
687
+
688
+ ```typescript
689
+ // ❌ 错误 - 无意义的 null 类型
690
+ interface OrderItem {
691
+ seatName: null
692
+ refundStatus: null
693
+ }
694
+
695
+ // ❌ 错误 - 无意义的 undefined 类型
696
+ interface Config {
697
+ value: undefined
698
+ }
699
+
700
+ // ❌ 错误 - 无意义的空字符串字面量类型
701
+ interface Query {
702
+ keyword: ''
703
+ }
704
+
705
+ // ✅ 正确 - 使用具体类型
706
+ interface OrderItem {
707
+ seatName: string
708
+ refundStatus: number
709
+ }
710
+
711
+ // ✅ 正确 - 联合类型中的 null / undefined 是有意义的(当 allowInUnions 为 true 时)
712
+ interface User {
713
+ phone: string | null
714
+ email?: string
715
+ }
716
+ ```
717
+
718
+ **配置选项:**
719
+
720
+ - `forbidden` (array, 必填): 要禁止的字面量类型列表
721
+ - 支持关键字类型:`'null'`、`'undefined'`、`'void'`、`'never'`、`'any'`、`'unknown'`、`'boolean'`、`'number'`、`'string'`、`'symbol'`、`'bigint'`、`'object'`
722
+ - 支持字面量值:`''`、`0`、`true`、`false` 等
723
+
724
+ - `allowInUnions` (boolean, 默认 `false`): 是否允许在联合类型中使用
725
+ - 为 `true` 时,`string | null` 不会被报错,但独立的 `null` 类型仍然会报错
726
+ - 为 `false` 时,任何出现 `null` / `undefined` 等的地方都会报错
727
+
728
+ **注意:**
729
+
730
+ - 该规则针对类型注解中的字面量,不影响运行时值,例如 `const a = null` 不会触发
731
+ - 建议配合 TypeScript 严格模式使用,逐步替换项目中的无意义类型声明
package/index.js CHANGED
@@ -9,6 +9,7 @@ const noExcessiveAnyRule = require('./lib/rules/no-excessive-any')
9
9
  const noDisabledCommentsRule = require('./lib/rules/no-disabled-comments')
10
10
  const noRestrictedComponentsRule = require('./lib/rules/no-restricted-components')
11
11
  const requireJsonParseTypeRule = require('./lib/rules/require-json-parse-type')
12
+ const noRestrictedLiteralTypesRule = require('./lib/rules/no-restricted-literal-types')
12
13
 
13
14
  // 限制属性,同时相当于限制某些组件禁止使用
14
15
  const restrictions = [
@@ -44,6 +45,7 @@ module.exports = {
44
45
  'no-disabled-comments': noDisabledCommentsRule,
45
46
  'no-restricted-components': noRestrictedComponentsRule,
46
47
  'require-json-parse-type': requireJsonParseTypeRule,
48
+ 'no-restricted-literal-types': noRestrictedLiteralTypesRule,
47
49
  },
48
50
  configs: {
49
51
  customize: (options) => {
@@ -1,3 +1,5 @@
1
+ const { getSourceCode } = require('../utils/eslint-compat')
2
+
1
3
  module.exports = {
2
4
  meta: {
3
5
  type: 'suggestion',
@@ -24,7 +26,7 @@ module.exports = {
24
26
  messageId: 'useArrowFunction',
25
27
  fix(fixer) {
26
28
  // 将 function 声明转换为箭头函数
27
- const sourceCode = context.getSourceCode()
29
+ const sourceCode = getSourceCode(context)
28
30
  const functionName = node.id.name
29
31
  const params = node.params.map(param => sourceCode.getText(param)).join(', ')
30
32
  const body = sourceCode.getText(node.body)
@@ -52,7 +54,7 @@ module.exports = {
52
54
  messageId: 'useArrowFunction',
53
55
  fix(fixer) {
54
56
  // 将函数表达式转换为箭头函数
55
- const sourceCode = context.getSourceCode()
57
+ const sourceCode = getSourceCode(context)
56
58
  const params = node.init.params.map(param => sourceCode.getText(param)).join(', ')
57
59
  const body = sourceCode.getText(node.init.body)
58
60
 
@@ -1,3 +1,5 @@
1
+ const { getFilename, getSourceCode } = require('../utils/eslint-compat')
2
+
1
3
  module.exports = {
2
4
  meta: {
3
5
  type: 'problem',
@@ -29,7 +31,7 @@ module.exports = {
29
31
  const maxExports = options.maxExports || 20
30
32
 
31
33
  // 只处理 .vue 文件
32
- const filename = context.getFilename()
34
+ const filename = getFilename(context)
33
35
  if (!filename.endsWith('.vue')) {
34
36
  return {}
35
37
  }
@@ -37,7 +39,7 @@ module.exports = {
37
39
  return {
38
40
  Program() {
39
41
  // 获取整个文件的源代码
40
- const sourceCode = context.getSourceCode()
42
+ const sourceCode = getSourceCode(context)
41
43
  const text = sourceCode.getText()
42
44
 
43
45
  // 使用正则表达式匹配所有 hooks 的解构赋值
@@ -1,3 +1,5 @@
1
+ const { getFilename, getSourceCode } = require('../utils/eslint-compat')
2
+
1
3
  module.exports = {
2
4
  meta: {
3
5
  type: 'problem',
@@ -100,7 +102,7 @@ module.exports = {
100
102
 
101
103
  return {
102
104
  Program() {
103
- const sourceCode = context.getSourceCode()
105
+ const sourceCode = getSourceCode(context)
104
106
  const comments = sourceCode.getAllComments()
105
107
 
106
108
  // 检查 JavaScript/TypeScript 注释
@@ -117,7 +119,7 @@ module.exports = {
117
119
  })
118
120
 
119
121
  // 检查 Vue 文件中的 HTML 注释
120
- const filename = context.getFilename()
122
+ const filename = getFilename(context)
121
123
  if (filename.endsWith('.vue')) {
122
124
  checkVueHtmlComments(context, sourceCode, regexPatterns)
123
125
  }
@@ -1,3 +1,5 @@
1
+ const { getSourceCode } = require('../utils/eslint-compat')
2
+
1
3
  module.exports = {
2
4
  meta: {
3
5
  type: 'problem',
@@ -208,7 +210,7 @@ const anyVariables = new Set()
208
210
  // 检查是否有类型参数(通过源代码文本检查)
209
211
  // 由于 Vue SFC 文件中的 TypeScript 泛型可能没有被正确解析为 AST 节点,
210
212
  // 我们通过检查源代码文本来判断是否有类型参数
211
- const sourceCode = context.getSourceCode()
213
+ const sourceCode = getSourceCode(context)
212
214
  const callText = sourceCode.getText(node)
213
215
 
214
216
  // 检查调用文本中是否包含泛型类型参数(如 <string>)
@@ -325,7 +327,7 @@ const anyVariables = new Set()
325
327
  */
326
328
  function getImportsForCurrentFile() {
327
329
  const imports = []
328
- const programNode = context.getSourceCode().ast
330
+ const programNode = getSourceCode(context).ast
329
331
 
330
332
  // 遍历所有导入声明
331
333
  programNode.body.forEach(node => {
@@ -529,7 +531,7 @@ const anyVariables = new Set()
529
531
 
530
532
  // 检查函数调用中的类型参数
531
533
  if (node.type === 'CallExpression') {
532
- const sourceCode = context.getSourceCode()
534
+ const sourceCode = getSourceCode(context)
533
535
  const callText = sourceCode.getText(node)
534
536
 
535
537
  // 检查类型参数(泛型)
@@ -1,3 +1,5 @@
1
+ const { getSourceCode } = require('../utils/eslint-compat');
2
+
1
3
  module.exports = {
2
4
  meta: {
3
5
  type: 'suggestion',
@@ -112,7 +114,7 @@ module.exports = {
112
114
  }
113
115
 
114
116
  // 获取类型注解的源代码
115
- const sourceCode = context.getSourceCode();
117
+ const sourceCode = getSourceCode(context);
116
118
  const typeAnnotationText = sourceCode.getText(node.typeAnnotation);
117
119
 
118
120
  // 检查是否匹配任何排除模式
@@ -1,3 +1,5 @@
1
+ const { getFilename, getSourceCode } = require('../utils/eslint-compat');
2
+
1
3
  module.exports = {
2
4
  meta: {
3
5
  type: 'problem',
@@ -64,7 +66,7 @@ module.exports = {
64
66
  });
65
67
 
66
68
  // 获取文件名,只对 .vue 文件生效
67
- const filename = context.getFilename();
69
+ const filename = getFilename(context);
68
70
  if (!filename.endsWith('.vue')) {
69
71
  return {};
70
72
  }
@@ -72,7 +74,7 @@ module.exports = {
72
74
  return {
73
75
  Program(node) {
74
76
  // 获取整个文件的源代码
75
- const sourceCode = context.getSourceCode();
77
+ const sourceCode = getSourceCode(context);
76
78
  const text = sourceCode.getText();
77
79
 
78
80
  // 使用正则表达式匹配模板中的标签
@@ -0,0 +1,22 @@
1
+ import { Rule } from 'eslint'
2
+
3
+ export interface NoRestrictedLiteralTypesOptions {
4
+ /**
5
+ * 要禁止的字面量类型列表
6
+ * @example ['null', 'undefined', '']
7
+ */
8
+ forbidden: Array<string | number | boolean>
9
+
10
+ /**
11
+ * 是否允许在联合类型中使用,例如 string | null
12
+ * @default false
13
+ */
14
+ allowInUnions?: boolean
15
+ }
16
+
17
+ /**
18
+ * 禁止在类型注解中使用指定的字面量类型
19
+ */
20
+ declare const rule: Rule.RuleModule
21
+
22
+ export default rule
@@ -0,0 +1,158 @@
1
+ module.exports = {
2
+ meta: {
3
+ type: 'suggestion',
4
+ docs: {
5
+ description: '禁止在类型注解中使用指定的字面量类型',
6
+ category: 'Best Practices',
7
+ recommended: false,
8
+ },
9
+ fixable: null,
10
+ schema: [
11
+ {
12
+ type: 'object',
13
+ properties: {
14
+ forbidden: {
15
+ type: 'array',
16
+ items: {
17
+ anyOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }],
18
+ },
19
+ description:
20
+ '要禁止的字面量类型列表,支持 null、undefined、void、never、any、unknown 等关键字,以及空字符串、0、true 等具体字面量',
21
+ },
22
+ allowInUnions: {
23
+ type: 'boolean',
24
+ default: false,
25
+ description: '是否允许在联合类型中使用,例如 string | null',
26
+ },
27
+ },
28
+ required: ['forbidden'],
29
+ additionalProperties: false,
30
+ },
31
+ ],
32
+ messages: {
33
+ noRestrictedLiteralType: '禁止在类型中使用 "{{type}}" 字面量类型',
34
+ },
35
+ },
36
+
37
+ create(context) {
38
+ const options = context.options[0] || {}
39
+ const forbidden = options.forbidden || []
40
+ const allowInUnions = options.allowInUnions || false
41
+
42
+ if (forbidden.length === 0) {
43
+ return {}
44
+ }
45
+
46
+ const forbiddenStrings = new Set()
47
+ const forbiddenNumbers = new Set()
48
+ const forbiddenBooleans = new Set()
49
+
50
+ forbidden.forEach((item) => {
51
+ if (typeof item === 'string') {
52
+ forbiddenStrings.add(item)
53
+ } else if (typeof item === 'number') {
54
+ forbiddenNumbers.add(item)
55
+ } else if (typeof item === 'boolean') {
56
+ forbiddenBooleans.add(item)
57
+ }
58
+ })
59
+
60
+ // 关键字类型映射
61
+ const keywordTypeMap = {
62
+ null: 'TSNullKeyword',
63
+ undefined: 'TSUndefinedKeyword',
64
+ void: 'TSVoidKeyword',
65
+ never: 'TSNeverKeyword',
66
+ any: 'TSAnyKeyword',
67
+ unknown: 'TSUnknownKeyword',
68
+ boolean: 'TSBooleanKeyword',
69
+ number: 'TSNumberKeyword',
70
+ string: 'TSStringKeyword',
71
+ symbol: 'TSSymbolKeyword',
72
+ bigint: 'TSBigIntKeyword',
73
+ object: 'TSObjectKeyword',
74
+ }
75
+
76
+ /**
77
+ * 检查类型是否位于联合类型中
78
+ * @param {Object} node AST 节点
79
+ * @returns {boolean}
80
+ */
81
+ function isInUnion(node) {
82
+ return node.parent && node.parent.type === 'TSUnionType'
83
+ }
84
+
85
+ /**
86
+ * 获取匹配到的 forbidden 值
87
+ * @param {Object} node AST 节点
88
+ * @returns {string|number|boolean|null}
89
+ */
90
+ function getMatchedForbidden(node) {
91
+ if (allowInUnions && isInUnion(node)) {
92
+ return null
93
+ }
94
+
95
+ // 关键字类型检查
96
+ for (const [key, typeName] of Object.entries(keywordTypeMap)) {
97
+ if (forbiddenStrings.has(key) && node.type === typeName) {
98
+ return key
99
+ }
100
+ }
101
+
102
+ // 字面量类型检查
103
+ if (node.type === 'TSLiteralType' && node.literal) {
104
+ const literal = node.literal
105
+
106
+ if (literal.type === 'Literal') {
107
+ if (typeof literal.value === 'string' && forbiddenStrings.has(literal.value)) {
108
+ return literal.value
109
+ }
110
+ if (typeof literal.value === 'number' && forbiddenNumbers.has(literal.value)) {
111
+ return literal.value
112
+ }
113
+ if (typeof literal.value === 'boolean' && forbiddenBooleans.has(literal.value)) {
114
+ return literal.value
115
+ }
116
+ }
117
+ }
118
+
119
+ return null
120
+ }
121
+
122
+ const targetTypes = [
123
+ 'TSNullKeyword',
124
+ 'TSUndefinedKeyword',
125
+ 'TSVoidKeyword',
126
+ 'TSNeverKeyword',
127
+ 'TSAnyKeyword',
128
+ 'TSUnknownKeyword',
129
+ 'TSBooleanKeyword',
130
+ 'TSNumberKeyword',
131
+ 'TSStringKeyword',
132
+ 'TSSymbolKeyword',
133
+ 'TSBigIntKeyword',
134
+ 'TSObjectKeyword',
135
+ 'TSLiteralType',
136
+ ]
137
+
138
+ const visitors = {}
139
+ targetTypes.forEach((type) => {
140
+ visitors[type] = function (node) {
141
+ const matched = getMatchedForbidden(node)
142
+ if (matched === null) {
143
+ return
144
+ }
145
+
146
+ context.report({
147
+ node,
148
+ messageId: 'noRestrictedLiteralType',
149
+ data: {
150
+ type: String(matched),
151
+ },
152
+ })
153
+ }
154
+ })
155
+
156
+ return visitors
157
+ },
158
+ }
@@ -80,6 +80,43 @@ module.exports = {
80
80
  return node
81
81
  }
82
82
 
83
+ /**
84
+ * 递归检查表达式中是否包含 JSON.parse(...) 调用
85
+ * 用于处理三元表达式、逻辑表达式、对象/数组字面量等场景
86
+ * @param {Object} node AST 节点
87
+ * @returns {boolean}
88
+ */
89
+ function containsJsonParse(node) {
90
+ if (!node || typeof node !== 'object') {
91
+ return false
92
+ }
93
+
94
+ const unwrapped = unwrapExpression(node)
95
+ if (isJsonParseCall(unwrapped)) {
96
+ return true
97
+ }
98
+
99
+ // 不进入函数体内部,函数体是独立作用域,不影响外层变量类型
100
+ if (unwrapped.type === 'ArrowFunctionExpression' || unwrapped.type === 'FunctionExpression') {
101
+ return false
102
+ }
103
+
104
+ return Object.keys(unwrapped).some((key) => {
105
+ if (key === 'parent' || key === 'loc' || key === 'range') {
106
+ return false
107
+ }
108
+
109
+ const value = unwrapped[key]
110
+ if (Array.isArray(value)) {
111
+ return value.some((child) => containsJsonParse(child))
112
+ }
113
+ if (value && typeof value === 'object' && value.type) {
114
+ return containsJsonParse(value)
115
+ }
116
+ return false
117
+ })
118
+ }
119
+
83
120
  /**
84
121
  * 检查变量声明是否使用了 JSON.parse 并且缺少显式类型注解
85
122
  * @param {Object} node VariableDeclaration 节点
@@ -94,8 +131,7 @@ module.exports = {
94
131
  return
95
132
  }
96
133
 
97
- const unwrappedInit = unwrapExpression(declaration.init)
98
- if (!isJsonParseCall(unwrappedInit)) {
134
+ if (!containsJsonParse(declaration.init)) {
99
135
  return
100
136
  }
101
137
 
@@ -1,3 +1,5 @@
1
+ const { getFilename, getSourceCode } = require('../utils/eslint-compat');
2
+
1
3
  module.exports = {
2
4
  meta: {
3
5
  type: 'problem',
@@ -63,7 +65,7 @@ module.exports = {
63
65
  }
64
66
 
65
67
  // 获取文件名,只对 .vue 文件生效
66
- const filename = context.getFilename();
68
+ const filename = getFilename(context);
67
69
  if (!filename.endsWith('.vue')) {
68
70
  return {};
69
71
  }
@@ -77,7 +79,7 @@ module.exports = {
77
79
  return {
78
80
  Program(node) {
79
81
  // 获取整个文件的源代码
80
- const sourceCode = context.getSourceCode();
82
+ const sourceCode = getSourceCode(context);
81
83
  const text = sourceCode.getText();
82
84
 
83
85
  // 使用正则表达式匹配模板中的标签和属性
@@ -1,3 +1,5 @@
1
+ const { getSourceCode } = require('../utils/eslint-compat');
2
+
1
3
  module.exports = {
2
4
  meta: {
3
5
  type: 'suggestion',
@@ -53,7 +55,7 @@ module.exports = {
53
55
  * @returns {boolean} 是否有注释
54
56
  */
55
57
  function hasComment(node) {
56
- const sourceCode = context.getSourceCode();
58
+ const sourceCode = getSourceCode(context);
57
59
  const comments = sourceCode.getCommentsBefore(node);
58
60
 
59
61
  // 检查是否有任何类型的注释(行注释 // 或块注释 /** */)
@@ -1,3 +1,5 @@
1
+ const { getFilename, getSourceCode } = require('../utils/eslint-compat');
2
+
1
3
  module.exports = {
2
4
  meta: {
3
5
  type: 'problem',
@@ -29,7 +31,7 @@ module.exports = {
29
31
  const maxLines = options.maxLines || 300;
30
32
 
31
33
  // 只处理 .vue 文件
32
- const filename = context.getFilename();
34
+ const filename = getFilename(context);
33
35
  if (!filename.endsWith('.vue')) {
34
36
  return {};
35
37
  }
@@ -37,7 +39,7 @@ module.exports = {
37
39
  return {
38
40
  Program(node) {
39
41
  // 获取整个文件的源代码
40
- const sourceCode = context.getSourceCode();
42
+ const sourceCode = getSourceCode(context);
41
43
  const text = sourceCode.getText();
42
44
 
43
45
  // 使用正则表达式匹配 script 标签
@@ -0,0 +1,46 @@
1
+ /**
2
+ * ESLint 8 / 9 / 10 兼容工具
3
+ *
4
+ * 背景:
5
+ * - `context.sourceCode`、`context.filename` 属性自 ESLint v8.40.0 引入
6
+ * - `context.getSourceCode()`、`context.getFilename()` 方法在 ESLint v9 中废弃,
7
+ * 并在 ESLint v10.0.0 中被彻底移除
8
+ *
9
+ * 因此这里优先读取新属性,不存在时回退到旧方法,
10
+ * 使规则可以同时运行在 ESLint 8(含 8.0 ~ 8.39)、9、10 三个大版本上。
11
+ */
12
+
13
+ /**
14
+ * 获取 SourceCode 实例(兼容 ESLint 8/9/10)
15
+ * @param {import('eslint').Rule.RuleContext} context 规则上下文
16
+ * @returns {import('eslint').SourceCode} SourceCode 实例
17
+ */
18
+ function getSourceCode(context) {
19
+ if (context.sourceCode) {
20
+ return context.sourceCode
21
+ }
22
+ if (typeof context.getSourceCode === 'function') {
23
+ return context.getSourceCode()
24
+ }
25
+ throw new Error('[@foundbyte/eslint-plugin] 无法从规则上下文获取 SourceCode')
26
+ }
27
+
28
+ /**
29
+ * 获取当前被 lint 文件的文件名(兼容 ESLint 8/9/10)
30
+ * @param {import('eslint').Rule.RuleContext} context 规则上下文
31
+ * @returns {string} 文件名
32
+ */
33
+ function getFilename(context) {
34
+ if (typeof context.filename === 'string') {
35
+ return context.filename
36
+ }
37
+ if (typeof context.getFilename === 'function') {
38
+ return context.getFilename()
39
+ }
40
+ return '<input>'
41
+ }
42
+
43
+ module.exports = {
44
+ getSourceCode,
45
+ getFilename,
46
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foundbyte/eslint-plugin",
3
- "version": "1.1.5-alpha.0",
3
+ "version": "1.1.5-alpha.2",
4
4
  "description": "ESLint plugin for enforcing code style rules including arrow function padding, Vue script lines limit, and TypeScript type comment requirements",
5
5
  "author": "",
6
6
  "license": "ISC",
@@ -18,13 +18,13 @@
18
18
  ],
19
19
  "main": "index.js",
20
20
  "types": "index.d.ts",
21
+ "scripts": {
22
+ "test": "echo \"Error: no test specified\" && exit 1"
23
+ },
21
24
  "publishConfig": {
22
25
  "access": "public"
23
26
  },
24
27
  "peerDependencies": {
25
- "eslint": ">=8.0.0"
26
- },
27
- "scripts": {
28
- "test": "echo \"Error: no test specified\" && exit 1"
28
+ "eslint": "^8.0.0 || ^9.0.0 || ^10.0.0"
29
29
  }
30
- }
30
+ }