@agilebot/eslint-plugin 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@agilebot/eslint-plugin",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Agilebot's ESLint plugin",
5
- "main": "lib",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
6
7
  "license": "MIT",
7
8
  "keywords": [
8
9
  "eslint",
@@ -19,16 +20,17 @@
19
20
  "dependencies": {
20
21
  "@typescript-eslint/utils": "^7.6.0",
21
22
  "eslint-plugin-react": "^7.34.1",
22
- "fast-glob": "^3.3.2",
23
- "@agilebot/eslint-utils": "0.2.2"
23
+ "@agilebot/eslint-utils": "0.2.3"
24
24
  },
25
25
  "peerDependencies": {
26
26
  "eslint": "^7.0.0 || ^8.0.0"
27
27
  },
28
28
  "files": [
29
- "lib"
29
+ "dist"
30
30
  ],
31
31
  "scripts": {
32
- "lint": "eslint lib --fix"
32
+ "build": "rollup -c rollup.config.mjs && nr dts",
33
+ "dts": "tsc -p tsconfig.dts.json",
34
+ "lint": "eslint src --fix"
33
35
  }
34
36
  }
package/lib/index.js DELETED
@@ -1,43 +0,0 @@
1
- // @ts-check
2
- const path = require('node:path');
3
- const { globSync } = require('fast-glob');
4
-
5
- // List all rules in rules folder
6
- const rulesPath = path.join(__dirname, 'rules');
7
- const ruleFiles = globSync('**/*.js', {
8
- cwd: rulesPath
9
- });
10
-
11
- /**
12
- * @type {import('eslint').Linter.RulesRecord}
13
- */
14
- const rules = {};
15
- ruleFiles.forEach(file => {
16
- const { dir, name } = path.parse(file);
17
- const ruleName = `${dir}/${name}`;
18
- rules[ruleName] = require(path.join(rulesPath, file));
19
- });
20
-
21
- module.exports.rules = rules;
22
-
23
- module.exports.configs = {
24
- recommended: {
25
- plugins: ['@agilebot'],
26
- rules: {
27
- '@agilebot/react/prefer-named-property-access': 'error',
28
- '@agilebot/react/hook-use-ref': 'warn',
29
- '@agilebot/react/no-inline-styles': 'error',
30
- '@agilebot/tss/unused-classes': 'warn',
31
- '@agilebot/tss/no-color-value': 'error',
32
- '@agilebot/tss/class-naming': 'error',
33
- '@agilebot/import/enforce-icon-alias': 'error',
34
- '@agilebot/import/monorepo': 'error',
35
- '@agilebot/others/no-unnecessary-template-literals': 'error'
36
- },
37
- settings: {
38
- react: {
39
- version: '18.0.0'
40
- }
41
- }
42
- }
43
- };
@@ -1,42 +0,0 @@
1
- module.exports = {
2
- meta: {
3
- type: 'problem', // `problem`, `suggestion`, or `layout`
4
- docs: {
5
- description: 'Enforce alias for @mui/icons-material imports',
6
- recommended: true
7
- },
8
- fixable: 'code', // Or `code` or `whitespace`
9
- schema: [] // Add a schema if the rule has options
10
- },
11
-
12
- create(context) {
13
- return {
14
- ImportDeclaration(node) {
15
- if (
16
- node.source.value !== '@mui/icons-material' &&
17
- node.source.value !== 'mdi-material-ui'
18
- ) {
19
- return;
20
- }
21
-
22
- for (const specifier of node.specifiers) {
23
- if (specifier.type !== 'ImportSpecifier') {
24
- return;
25
- }
26
-
27
- if (specifier.imported.name === specifier.local.name) {
28
- context.report({
29
- node,
30
- message: `Import for ${node.source.value} should be aliased.`,
31
- fix: fixer =>
32
- fixer.replaceText(
33
- specifier,
34
- `${specifier.imported.name} as ${specifier.imported.name}Icon`
35
- )
36
- });
37
- }
38
- }
39
- }
40
- };
41
- }
42
- };
@@ -1,49 +0,0 @@
1
- const { getSetting } = require('../../util/settings');
2
-
3
- let warnedForMissingPrefix = false;
4
-
5
- module.exports = {
6
- meta: {
7
- type: 'problem', // `problem`, `suggestion`, or `layout`
8
- docs: {
9
- description: 'Enforce import styles for monorepo',
10
- recommended: true
11
- },
12
- fixable: 'code', // Or `code` or `whitespace`
13
- schema: [] // Add a schema if the rule has options
14
- },
15
-
16
- create(context) {
17
- return {
18
- ImportDeclaration(node) {
19
- const prefix = getSetting(context, 'monorepo-prefix');
20
- if (!prefix) {
21
- if (!warnedForMissingPrefix) {
22
- // eslint-disable-next-line no-console -- this is a CLI tool
23
- console.error('Warning: agilebot/monorepo-prefix is not set.');
24
- warnedForMissingPrefix = true;
25
- }
26
- return;
27
- }
28
-
29
- if (!node.source.value.startsWith(prefix)) {
30
- return;
31
- }
32
- const values = node.source.value.split('/');
33
-
34
- if (values[2] === 'src') {
35
- context.report({
36
- node,
37
- message: `Import for ${node.source.value} should not contains src folder.`,
38
- fix: fixer => {
39
- const correctedPath = values
40
- .filter((_, index) => index !== 2)
41
- .join('/');
42
- return fixer.replaceText(node.source, `'${correctedPath}'`);
43
- }
44
- });
45
- }
46
- }
47
- };
48
- }
49
- };
@@ -1,111 +0,0 @@
1
- const {
2
- sortedTemplateElements,
3
- templateLiteralDisplayStr,
4
- findFormatMessageAttrNode,
5
- findFormattedMessageAttrNode,
6
- findAttrNodeInDefineMessages,
7
- findAttrNodeInDefineMessage
8
- } = require('../../util/intl');
9
- const { getIntlIds } = require('../../util/translations');
10
-
11
- // ------------------------------------------------------------------------------
12
- // Rule Definition
13
- // ------------------------------------------------------------------------------
14
-
15
- module.exports = {
16
- meta: {
17
- docs: {
18
- description: 'Validates intl message ids are in locale file',
19
- category: 'Intl',
20
- recommended: true
21
- },
22
- fixable: null,
23
- schema: []
24
- },
25
-
26
- create: function (context) {
27
- const translatedIds = getIntlIds(context);
28
- const translatedIdSet = new Set(translatedIds);
29
-
30
- // ----------------------------------------------------------------------
31
- // Helpers
32
- // ----------------------------------------------------------------------
33
-
34
- function isLiteralTranslated(id) {
35
- return translatedIdSet.has(id);
36
- }
37
-
38
- function isTemplateTranslated(re) {
39
- return translatedIds.some(k => re.test(k));
40
- }
41
-
42
- function processLiteral(node) {
43
- if (!isLiteralTranslated(node.value)) {
44
- context.report({
45
- node: node,
46
- message: 'Missing id: ' + node.value
47
- });
48
- }
49
- }
50
-
51
- function processTemplateLiteral(node) {
52
- const exStr = sortedTemplateElements(node)
53
- .map(e => (!e.value ? '.*' : e.value.raw))
54
- .join('');
55
- const re = new RegExp(exStr);
56
-
57
- if (!isTemplateTranslated(re)) {
58
- context.report({
59
- node: node,
60
- message: 'Missing id pattern: ' + templateLiteralDisplayStr(node)
61
- });
62
- }
63
- }
64
-
65
- function processAttrNode(node) {
66
- if (node.value.type === 'Literal') {
67
- return processLiteral(node.value);
68
- }
69
- if (
70
- node.value.type === 'JSXExpressionContainer' &&
71
- node.value.expression.type === 'TemplateLiteral'
72
- ) {
73
- return processTemplateLiteral(node.value.expression);
74
- }
75
- if (node.value.type === 'TemplateLiteral') {
76
- return processTemplateLiteral(node.value);
77
- }
78
- context.report({
79
- node: node,
80
- message: 'Do not invoke intl by ' + node.value.type
81
- });
82
- }
83
-
84
- // ----------------------------------------------------------------------
85
- // Public
86
- // ----------------------------------------------------------------------
87
-
88
- return {
89
- JSXIdentifier: function (node) {
90
- const attrNode = findFormattedMessageAttrNode(node, 'id');
91
- if (attrNode) {
92
- return processAttrNode(attrNode);
93
- }
94
- },
95
- CallExpression: function (node) {
96
- const attrNode = findFormatMessageAttrNode(node, 'id');
97
- if (attrNode) {
98
- return processAttrNode(attrNode);
99
- }
100
- },
101
- Property: function (node) {
102
- const attrNode =
103
- findAttrNodeInDefineMessages(node, 'id') ||
104
- findAttrNodeInDefineMessage(node, 'id');
105
- if (attrNode) {
106
- return processAttrNode(attrNode);
107
- }
108
- }
109
- };
110
- }
111
- };
@@ -1,103 +0,0 @@
1
- const {
2
- findFormatMessageAttrNode,
3
- findFormattedMessageAttrNode,
4
- findAttrNodeInDefineMessages,
5
- findAttrNodeInDefineMessage,
6
- templateLiteralDisplayStr
7
- } = require('../../util/intl');
8
-
9
- // ------------------------------------------------------------------------------
10
- // Rule Definition
11
- // ------------------------------------------------------------------------------
12
-
13
- module.exports = {
14
- meta: {
15
- docs: {
16
- description: 'Validates intl message ids has correct prefixes',
17
- category: 'Intl',
18
- recommended: true
19
- },
20
- fixable: null,
21
- schema: [
22
- {
23
- type: 'array',
24
- items: {
25
- type: 'string'
26
- }
27
- }
28
- ]
29
- },
30
-
31
- create: function (context) {
32
- if (context.options[0].length === 0) {
33
- throw new Error('Prefixes are required in settings');
34
- }
35
-
36
- // ----------------------------------------------------------------------
37
- // Helpers
38
- // ----------------------------------------------------------------------
39
-
40
- const hasPrefix = value =>
41
- context.options[0].some(p => value.startsWith(p));
42
-
43
- function report(node, value) {
44
- if (!hasPrefix(value)) {
45
- context.report({
46
- node: node,
47
- message: `Invalid id prefix: ${value}`
48
- });
49
- }
50
- }
51
-
52
- function processLiteral(node) {
53
- report(node, node.value);
54
- }
55
-
56
- function processTemplateLiteral(node) {
57
- const displayStr = templateLiteralDisplayStr(node);
58
- report(node, displayStr);
59
- }
60
-
61
- function processAttrNode(node) {
62
- if (node.value.type === 'Literal') {
63
- return processLiteral(node.value);
64
- }
65
- if (
66
- node.value.type === 'JSXExpressionContainer' &&
67
- node.value.expression.type === 'TemplateLiteral'
68
- ) {
69
- return processTemplateLiteral(node.value.expression);
70
- }
71
- if (node.value.type === 'TemplateLiteral') {
72
- return processTemplateLiteral(node.value);
73
- }
74
- }
75
-
76
- // ----------------------------------------------------------------------
77
- // Public
78
- // ----------------------------------------------------------------------
79
-
80
- return {
81
- JSXIdentifier: function (node) {
82
- const attrNode = findFormattedMessageAttrNode(node, 'id');
83
- if (attrNode) {
84
- return processAttrNode(attrNode);
85
- }
86
- },
87
- CallExpression: function (node) {
88
- const attrNode = findFormatMessageAttrNode(node, 'id');
89
- if (attrNode) {
90
- return processAttrNode(attrNode);
91
- }
92
- },
93
- Property: function (node) {
94
- const attrNode =
95
- findAttrNodeInDefineMessages(node, 'id') ||
96
- findAttrNodeInDefineMessage(node, 'id');
97
- if (attrNode) {
98
- return processAttrNode(attrNode);
99
- }
100
- }
101
- };
102
- }
103
- };
@@ -1,123 +0,0 @@
1
- const path = require('node:path');
2
- const fs = require('node:fs');
3
- const {
4
- sortedTemplateElements,
5
- findFormatMessageAttrNode,
6
- findFormattedMessageAttrNode,
7
- findAttrNodeInDefineMessages,
8
- findAttrNodeInDefineMessage
9
- } = require('../../util/intl');
10
- const { getIntlIds } = require('../../util/translations');
11
- const { getSetting } = require('../../util/settings');
12
-
13
- // 已经使用的id集合 key为项目目录,value为id集合Set
14
- const usedIds = new Map();
15
-
16
- // ------------------------------------------------------------------------------
17
- // Rule Definition
18
- // ------------------------------------------------------------------------------
19
-
20
- module.exports = {
21
- meta: {
22
- docs: {
23
- description: 'Finds unused intl message ids in locale file',
24
- category: 'Intl',
25
- recommended: true
26
- },
27
- fixable: null,
28
- schema: []
29
- },
30
-
31
- create: function (context) {
32
- const projectRoot = getSetting(context, 'project-root');
33
- if (!projectRoot) {
34
- throw new Error('projectRoot must be set in this rule');
35
- }
36
- if (!usedIds.has(projectRoot)) {
37
- usedIds.set(projectRoot, new Set());
38
- }
39
- const usedIdSet = usedIds.get(projectRoot);
40
-
41
- const translatedIds = getIntlIds(context);
42
- const translatedIdSet = new Set(translatedIds);
43
-
44
- // ----------------------------------------------------------------------
45
- // Helpers
46
- // ----------------------------------------------------------------------
47
-
48
- function isLiteralTranslated(id) {
49
- return translatedIdSet.has(id);
50
- }
51
-
52
- function isTemplateTranslated(re) {
53
- return translatedIds.some(k => re.test(k));
54
- }
55
-
56
- function processLiteral(node) {
57
- if (isLiteralTranslated(node.value)) {
58
- // 将已使用的id加入usedIdSet
59
- usedIdSet.add(node.value);
60
- }
61
- }
62
-
63
- function processTemplateLiteral(node) {
64
- const exStr = sortedTemplateElements(node)
65
- .map(e => (!e.value ? '.*' : e.value.raw))
66
- .join('');
67
- const re = new RegExp(exStr);
68
-
69
- if (isTemplateTranslated(re)) {
70
- // TODO: 暂时没有这种情况
71
- }
72
- }
73
-
74
- function processAttrNode(node) {
75
- if (node.value.type === 'Literal') {
76
- return processLiteral(node.value);
77
- }
78
- if (
79
- node.value.type === 'JSXExpressionContainer' &&
80
- node.value.expression.type === 'TemplateLiteral'
81
- ) {
82
- return processTemplateLiteral(node.value.expression);
83
- }
84
- if (node.value.type === 'TemplateLiteral') {
85
- return processTemplateLiteral(node.value);
86
- }
87
- }
88
-
89
- // ----------------------------------------------------------------------
90
- // Public
91
- // ----------------------------------------------------------------------
92
-
93
- return {
94
- JSXIdentifier: function (node) {
95
- const attrNode = findFormattedMessageAttrNode(node, 'id');
96
- if (attrNode) {
97
- return processAttrNode(attrNode);
98
- }
99
- },
100
- CallExpression: function (node) {
101
- const attrNode = findFormatMessageAttrNode(node, 'id');
102
- if (attrNode) {
103
- return processAttrNode(attrNode);
104
- }
105
- },
106
- Property: function (node) {
107
- const attrNode =
108
- findAttrNodeInDefineMessages(node, 'id') ||
109
- findAttrNodeInDefineMessage(node, 'id');
110
- if (attrNode) {
111
- return processAttrNode(attrNode);
112
- }
113
- },
114
- 'Program:exit': function () {
115
- // 将usedIdSet转为数组,然后与translatedIds取差集,即为未使用的id
116
- const unusedIds = [...translatedIdSet].filter(id => !usedIdSet.has(id));
117
- // 在项目目录下生成intl-unused.json
118
- const jsonPath = path.join(projectRoot, 'intl-unused.json');
119
- fs.writeFileSync(jsonPath, JSON.stringify(unusedIds, null, 2));
120
- }
121
- };
122
- }
123
- };
@@ -1,63 +0,0 @@
1
- const {
2
- findFormatMessageAttrNode,
3
- findFormattedMessageAttrNode,
4
- findAttrNodeInDefineMessages,
5
- findAttrNodeInDefineMessage
6
- } = require('../../util/intl');
7
-
8
- // ------------------------------------------------------------------------------
9
- // Rule Definition
10
- // ------------------------------------------------------------------------------
11
-
12
- module.exports = {
13
- meta: {
14
- docs: {
15
- description: 'Validates defaultMessage is not used with react-intl',
16
- category: 'Intl',
17
- recommended: true
18
- },
19
- fixable: null,
20
- schema: []
21
- },
22
-
23
- create: function (context) {
24
- // ----------------------------------------------------------------------
25
- // Helpers
26
- // ----------------------------------------------------------------------
27
-
28
- function processAttrNode(node) {
29
- context.report({
30
- node: node,
31
- message: 'Do not use defaultMessage'
32
- });
33
- }
34
-
35
- // ----------------------------------------------------------------------
36
- // Public
37
- // ----------------------------------------------------------------------
38
-
39
- return {
40
- JSXIdentifier: function (node) {
41
- const attrNode = findFormattedMessageAttrNode(node, 'defaultMessage');
42
- if (attrNode) {
43
- return processAttrNode(attrNode);
44
- }
45
- },
46
- CallExpression: function (node) {
47
- const attrNode = findFormatMessageAttrNode(node, 'defaultMessage');
48
- if (attrNode) {
49
- return processAttrNode(attrNode);
50
- }
51
- },
52
- Property: function (node) {
53
- const attrNode =
54
- findAttrNodeInDefineMessages(node, 'defaultMessage') ||
55
- findAttrNodeInDefineMessage(node, 'defaultMessage');
56
-
57
- if (attrNode) {
58
- return processAttrNode(attrNode);
59
- }
60
- }
61
- };
62
- }
63
- };
@@ -1,38 +0,0 @@
1
- module.exports = {
2
- meta: {
3
- type: 'problem',
4
- docs: {
5
- description: 'Check if a template string contains only one ${}',
6
- recommended: true
7
- },
8
- fixable: 'code', // Or `code` or `whitespace`
9
- schema: [] // Add a schema if the rule has options
10
- },
11
-
12
- create(context) {
13
- return {
14
- TemplateLiteral(node) {
15
- // Get the entire source code of the template string
16
- const code = context.getSourceCode().getText(node);
17
- // Check if the template string contains only one `${}`
18
- if (
19
- code.startsWith('`${') &&
20
- code.endsWith('}`') &&
21
- code.split('${').length === 2
22
- ) {
23
- context.report({
24
- node,
25
- message: 'Unnecessary template string with only one ${}.',
26
- fix(fixer) {
27
- // Replace the entire template string with the content inside ${}
28
- return fixer.replaceText(
29
- node,
30
- code.substring(3, code.length - 2)
31
- );
32
- }
33
- });
34
- }
35
- }
36
- };
37
- }
38
- };
@@ -1,35 +0,0 @@
1
- const Components = require('eslint-plugin-react/lib/util/Components');
2
-
3
- module.exports = {
4
- meta: {
5
- docs: {
6
- description: 'Ensure naming of useRef hook value.',
7
- recommended: false
8
- },
9
- schema: [],
10
- type: 'suggestion',
11
- hasSuggestions: true
12
- },
13
-
14
- create: Components.detect((context, component, util) => ({
15
- CallExpression(node) {
16
- const isImmediateReturn =
17
- node.parent && node.parent.type === 'ReturnStatement';
18
-
19
- if (isImmediateReturn || !util.isReactHookCall(node, ['useRef'])) {
20
- return;
21
- }
22
- if (node.parent.id.type !== 'Identifier') {
23
- return;
24
- }
25
- const variable = node.parent.id.name;
26
-
27
- if (!variable.endsWith('Ref')) {
28
- context.report({
29
- node: node,
30
- message: 'useRef call is not end with "Ref"'
31
- });
32
- }
33
- }
34
- }))
35
- };