@agilebot/eslint-plugin 0.1.1 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
package/lib/index.js CHANGED
@@ -1,52 +1,59 @@
1
- const path = require('path');
2
- const { globSync } = require('fast-glob');
3
-
4
- // List all rules in rules folder
5
- const rulesPath = path.join(__dirname, 'rules');
6
- const ruleFiles = globSync('**/*.js', {
7
- cwd: rulesPath,
8
- depth: 2
9
- });
10
- const rules = {};
11
- ruleFiles.forEach(file => {
12
- const { dir, name } = path.parse(file);
13
- const ruleName = `${dir}/${name}`;
14
- rules[ruleName] = require(path.join(rulesPath, file));
15
- });
16
-
17
- module.exports.rules = rules;
18
-
19
- module.exports.configs = {
20
- recommended: {
21
- plugins: ['@agilebot'],
22
- rules: {
23
- 'react-hooks/exhaustive-deps': 'off',
24
- '@agilebot/react/better-exhaustive-deps': [
25
- 'warn',
26
- {
27
- checkMemoizedVariableIsStatic: true,
28
- staticHooks: {
29
- useIpcSender: true,
30
- useDialog: true,
31
- useSnackbar: true,
32
- useForm: true,
33
- 'use.*Store': {
34
- value: true,
35
- regexp: true
36
- }
37
- }
38
- }
39
- ],
40
- '@agilebot/react/prefer-named-property-access': 'error',
41
- '@agilebot/react/hook-use-ref': 'warn',
42
- '@agilebot/react/no-inline-styles': 'error',
43
- '@agilebot/tss/unused-classes': 'warn',
44
- '@agilebot/tss/no-color-value': 'error',
45
- '@agilebot/tss/class-naming': 'error',
46
- '@agilebot/import/enforce-icon-alias': 'error',
47
- '@agilebot/import/monorepo': 'error',
48
- '@agilebot/others/no-unnecessary-template-literals': 'error'
49
- },
50
- settings: {}
51
- }
52
- };
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
+ 'react-hooks/exhaustive-deps': 'off',
28
+ '@agilebot/react/better-exhaustive-deps': [
29
+ 'warn',
30
+ {
31
+ checkMemoizedVariableIsStatic: true,
32
+ staticHooks: {
33
+ 'useIpc.*': {
34
+ value: true,
35
+ regexp: true
36
+ },
37
+ useDialog: true,
38
+ useSnackbar: true,
39
+ useForm: true,
40
+ 'use.*Store': {
41
+ value: true,
42
+ regexp: true
43
+ }
44
+ }
45
+ }
46
+ ],
47
+ '@agilebot/react/prefer-named-property-access': 'error',
48
+ '@agilebot/react/hook-use-ref': 'warn',
49
+ '@agilebot/react/no-inline-styles': 'error',
50
+ '@agilebot/tss/unused-classes': 'warn',
51
+ '@agilebot/tss/no-color-value': 'error',
52
+ '@agilebot/tss/class-naming': 'error',
53
+ '@agilebot/import/enforce-icon-alias': 'error',
54
+ '@agilebot/import/monorepo': 'error',
55
+ '@agilebot/others/no-unnecessary-template-literals': 'error'
56
+ },
57
+ settings: {}
58
+ }
59
+ };
@@ -1,44 +1,49 @@
1
- const { consola } = require('consola');
2
- const { getSetting } = require('../../util/settings');
3
-
4
- module.exports = {
5
- meta: {
6
- type: 'problem', // `problem`, `suggestion`, or `layout`
7
- docs: {
8
- description: 'Enforce import styles for monorepo',
9
- recommended: true
10
- },
11
- fixable: 'code', // Or `code` or `whitespace`
12
- schema: [] // Add a schema if the rule has options
13
- },
14
-
15
- create(context) {
16
- return {
17
- ImportDeclaration(node) {
18
- const prefix = getSetting(context, 'monorepo-prefix');
19
- if (!prefix) {
20
- consola.warn('agilebot/monorepo-prefix is not set');
21
- return;
22
- }
23
-
24
- if (!node.source.value.startsWith(prefix)) {
25
- return;
26
- }
27
- const values = node.source.value.split('/');
28
-
29
- if (values[2] === 'src') {
30
- context.report({
31
- node,
32
- message: `Import for ${node.source.value} should not contains src folder.`,
33
- fix: fixer => {
34
- const correctedPath = values
35
- .filter((_, index) => index !== 2)
36
- .join('/');
37
- return fixer.replaceText(node.source, `'${correctedPath}'`);
38
- }
39
- });
40
- }
41
- }
42
- };
43
- }
44
- };
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,117 +1,117 @@
1
- const path = require('path');
2
- const fs = require('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) return processAttrNode(attrNode);
97
- },
98
- CallExpression: function (node) {
99
- const attrNode = findFormatMessageAttrNode(node, 'id');
100
- if (attrNode) return processAttrNode(attrNode);
101
- },
102
- Property: function (node) {
103
- const attrNode =
104
- findAttrNodeInDefineMessages(node, 'id') ||
105
- findAttrNodeInDefineMessage(node, 'id');
106
- if (attrNode) return processAttrNode(attrNode);
107
- },
108
- 'Program:exit': function () {
109
- // 将usedIdSet转为数组,然后与translatedIds取差集,即为未使用的id
110
- const unusedIds = [...translatedIdSet].filter(id => !usedIdSet.has(id));
111
- // 在项目目录下生成intl-unused.json
112
- const jsonPath = path.join(projectRoot, 'intl-unused.json');
113
- fs.writeFileSync(jsonPath, JSON.stringify(unusedIds, null, 2));
114
- }
115
- };
116
- }
117
- };
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) return processAttrNode(attrNode);
97
+ },
98
+ CallExpression: function (node) {
99
+ const attrNode = findFormatMessageAttrNode(node, 'id');
100
+ if (attrNode) return processAttrNode(attrNode);
101
+ },
102
+ Property: function (node) {
103
+ const attrNode =
104
+ findAttrNodeInDefineMessages(node, 'id') ||
105
+ findAttrNodeInDefineMessage(node, 'id');
106
+ if (attrNode) return processAttrNode(attrNode);
107
+ },
108
+ 'Program:exit': function () {
109
+ // 将usedIdSet转为数组,然后与translatedIds取差集,即为未使用的id
110
+ const unusedIds = [...translatedIdSet].filter(id => !usedIdSet.has(id));
111
+ // 在项目目录下生成intl-unused.json
112
+ const jsonPath = path.join(projectRoot, 'intl-unused.json');
113
+ fs.writeFileSync(jsonPath, JSON.stringify(unusedIds, null, 2));
114
+ }
115
+ };
116
+ }
117
+ };