@agilebot/eslint-plugin 0.1.5 → 0.2.0
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/LICENSE +9 -9
- package/README.md +11 -11
- package/lib/index.js +58 -59
- package/lib/rules/import/enforce-icon-alias.js +42 -43
- package/lib/rules/import/monorepo.js +49 -49
- package/lib/rules/intl/id-missing.js +111 -105
- package/lib/rules/intl/id-prefix.js +103 -97
- package/lib/rules/intl/id-unused.js +123 -117
- package/lib/rules/intl/no-default.js +63 -57
- package/lib/rules/others/no-unnecessary-template-literals.js +38 -38
- package/lib/rules/react/better-exhaustive-deps.js +1923 -1921
- package/lib/rules/react/hook-use-ref.js +35 -37
- package/lib/rules/react/no-inline-styles.js +87 -87
- package/lib/rules/react/prefer-named-property-access.js +105 -105
- package/lib/rules/tss/class-naming.js +43 -43
- package/lib/rules/tss/no-color-value.js +58 -58
- package/lib/rules/tss/unused-classes.js +108 -108
- package/lib/util/intl.js +127 -127
- package/lib/util/settings.js +14 -14
- package/lib/util/translations.js +66 -66
- package/lib/util/tss.js +109 -109
- package/package.json +5 -3
@@ -1,108 +1,108 @@
|
|
1
|
-
const { getBasicIdentifier, getStyesObj } = require('../../util/tss');
|
2
|
-
|
3
|
-
module.exports = {
|
4
|
-
meta: {
|
5
|
-
type: 'problem'
|
6
|
-
},
|
7
|
-
create: function rule(context) {
|
8
|
-
const usedClasses = {};
|
9
|
-
const definedClasses = {};
|
10
|
-
|
11
|
-
return {
|
12
|
-
CallExpression(node) {
|
13
|
-
const stylesObj = getStyesObj(node);
|
14
|
-
|
15
|
-
if (stylesObj === undefined) {
|
16
|
-
return;
|
17
|
-
}
|
18
|
-
|
19
|
-
stylesObj.properties.forEach(property => {
|
20
|
-
if (property.computed) {
|
21
|
-
// Skip over computed properties for now.
|
22
|
-
// e.g. `{ [foo]: { ... } }`
|
23
|
-
return;
|
24
|
-
}
|
25
|
-
|
26
|
-
if (
|
27
|
-
property.type === 'ExperimentalSpreadProperty' ||
|
28
|
-
property.type === 'SpreadElement'
|
29
|
-
) {
|
30
|
-
// Skip over object spread for now.
|
31
|
-
// e.g. `{ ...foo }`
|
32
|
-
return;
|
33
|
-
}
|
34
|
-
definedClasses[property.key.value || property.key.name] = property;
|
35
|
-
});
|
36
|
-
},
|
37
|
-
|
38
|
-
MemberExpression(node) {
|
39
|
-
if (
|
40
|
-
node.object.type === 'Identifier' &&
|
41
|
-
node.object.name === 'classes'
|
42
|
-
) {
|
43
|
-
const whichClass = getBasicIdentifier(node.property);
|
44
|
-
if (whichClass) {
|
45
|
-
usedClasses[whichClass] = true;
|
46
|
-
}
|
47
|
-
return;
|
48
|
-
}
|
49
|
-
|
50
|
-
const classIdentifier = getBasicIdentifier(node.property);
|
51
|
-
if (!classIdentifier) {
|
52
|
-
// props['foo' + bar].baz
|
53
|
-
return;
|
54
|
-
}
|
55
|
-
|
56
|
-
if (classIdentifier !== 'classes') {
|
57
|
-
// props.foo.bar
|
58
|
-
return;
|
59
|
-
}
|
60
|
-
|
61
|
-
const { parent } = node;
|
62
|
-
|
63
|
-
if (parent.type !== 'MemberExpression') {
|
64
|
-
// foo.styles
|
65
|
-
return;
|
66
|
-
}
|
67
|
-
|
68
|
-
if (
|
69
|
-
node.object.object &&
|
70
|
-
node.object.object.type !== 'ThisExpression'
|
71
|
-
) {
|
72
|
-
// foo.foo.styles
|
73
|
-
return;
|
74
|
-
}
|
75
|
-
|
76
|
-
const propsIdentifier = getBasicIdentifier(parent.object);
|
77
|
-
if (propsIdentifier && propsIdentifier !== 'props') {
|
78
|
-
return;
|
79
|
-
}
|
80
|
-
if (!propsIdentifier && parent.object.type !== 'MemberExpression') {
|
81
|
-
return;
|
82
|
-
}
|
83
|
-
|
84
|
-
if (parent.parent.type === 'MemberExpression') {
|
85
|
-
// this.props.props.styles
|
86
|
-
return;
|
87
|
-
}
|
88
|
-
|
89
|
-
const parentClassIdentifier = getBasicIdentifier(parent.property);
|
90
|
-
if (parentClassIdentifier) {
|
91
|
-
usedClasses[parentClassIdentifier] = true;
|
92
|
-
}
|
93
|
-
},
|
94
|
-
'Program:exit': () => {
|
95
|
-
// Now we know all of the defined classes and used classes, so we can
|
96
|
-
// see if there are any defined classes that are not used.
|
97
|
-
Object.keys(definedClasses).forEach(definedClassKey => {
|
98
|
-
if (!usedClasses[definedClassKey]) {
|
99
|
-
context.report(
|
100
|
-
definedClasses[definedClassKey],
|
101
|
-
`Class \`${definedClassKey}\` is unused`
|
102
|
-
);
|
103
|
-
}
|
104
|
-
});
|
105
|
-
}
|
106
|
-
};
|
107
|
-
}
|
108
|
-
};
|
1
|
+
const { getBasicIdentifier, getStyesObj } = require('../../util/tss');
|
2
|
+
|
3
|
+
module.exports = {
|
4
|
+
meta: {
|
5
|
+
type: 'problem'
|
6
|
+
},
|
7
|
+
create: function rule(context) {
|
8
|
+
const usedClasses = {};
|
9
|
+
const definedClasses = {};
|
10
|
+
|
11
|
+
return {
|
12
|
+
CallExpression(node) {
|
13
|
+
const stylesObj = getStyesObj(node);
|
14
|
+
|
15
|
+
if (stylesObj === undefined) {
|
16
|
+
return;
|
17
|
+
}
|
18
|
+
|
19
|
+
stylesObj.properties.forEach(property => {
|
20
|
+
if (property.computed) {
|
21
|
+
// Skip over computed properties for now.
|
22
|
+
// e.g. `{ [foo]: { ... } }`
|
23
|
+
return;
|
24
|
+
}
|
25
|
+
|
26
|
+
if (
|
27
|
+
property.type === 'ExperimentalSpreadProperty' ||
|
28
|
+
property.type === 'SpreadElement'
|
29
|
+
) {
|
30
|
+
// Skip over object spread for now.
|
31
|
+
// e.g. `{ ...foo }`
|
32
|
+
return;
|
33
|
+
}
|
34
|
+
definedClasses[property.key.value || property.key.name] = property;
|
35
|
+
});
|
36
|
+
},
|
37
|
+
|
38
|
+
MemberExpression(node) {
|
39
|
+
if (
|
40
|
+
node.object.type === 'Identifier' &&
|
41
|
+
node.object.name === 'classes'
|
42
|
+
) {
|
43
|
+
const whichClass = getBasicIdentifier(node.property);
|
44
|
+
if (whichClass) {
|
45
|
+
usedClasses[whichClass] = true;
|
46
|
+
}
|
47
|
+
return;
|
48
|
+
}
|
49
|
+
|
50
|
+
const classIdentifier = getBasicIdentifier(node.property);
|
51
|
+
if (!classIdentifier) {
|
52
|
+
// props['foo' + bar].baz
|
53
|
+
return;
|
54
|
+
}
|
55
|
+
|
56
|
+
if (classIdentifier !== 'classes') {
|
57
|
+
// props.foo.bar
|
58
|
+
return;
|
59
|
+
}
|
60
|
+
|
61
|
+
const { parent } = node;
|
62
|
+
|
63
|
+
if (parent.type !== 'MemberExpression') {
|
64
|
+
// foo.styles
|
65
|
+
return;
|
66
|
+
}
|
67
|
+
|
68
|
+
if (
|
69
|
+
node.object.object &&
|
70
|
+
node.object.object.type !== 'ThisExpression'
|
71
|
+
) {
|
72
|
+
// foo.foo.styles
|
73
|
+
return;
|
74
|
+
}
|
75
|
+
|
76
|
+
const propsIdentifier = getBasicIdentifier(parent.object);
|
77
|
+
if (propsIdentifier && propsIdentifier !== 'props') {
|
78
|
+
return;
|
79
|
+
}
|
80
|
+
if (!propsIdentifier && parent.object.type !== 'MemberExpression') {
|
81
|
+
return;
|
82
|
+
}
|
83
|
+
|
84
|
+
if (parent.parent.type === 'MemberExpression') {
|
85
|
+
// this.props.props.styles
|
86
|
+
return;
|
87
|
+
}
|
88
|
+
|
89
|
+
const parentClassIdentifier = getBasicIdentifier(parent.property);
|
90
|
+
if (parentClassIdentifier) {
|
91
|
+
usedClasses[parentClassIdentifier] = true;
|
92
|
+
}
|
93
|
+
},
|
94
|
+
'Program:exit': () => {
|
95
|
+
// Now we know all of the defined classes and used classes, so we can
|
96
|
+
// see if there are any defined classes that are not used.
|
97
|
+
Object.keys(definedClasses).forEach(definedClassKey => {
|
98
|
+
if (!usedClasses[definedClassKey]) {
|
99
|
+
context.report(
|
100
|
+
definedClasses[definedClassKey],
|
101
|
+
`Class \`${definedClassKey}\` is unused`
|
102
|
+
);
|
103
|
+
}
|
104
|
+
});
|
105
|
+
}
|
106
|
+
};
|
107
|
+
}
|
108
|
+
};
|
package/lib/util/intl.js
CHANGED
@@ -1,127 +1,127 @@
|
|
1
|
-
/**
|
2
|
-
* Finds an attribute in formatMessage using attribute name.
|
3
|
-
*
|
4
|
-
* @param {Object} node - parent formatMessage node
|
5
|
-
* @param {string} attrName - attribute name.
|
6
|
-
* @returns {Object} node - returns node if it finds the attribute.
|
7
|
-
*/
|
8
|
-
function findFormatMessageAttrNode(node, attrName) {
|
9
|
-
// Find formatMessage usages
|
10
|
-
if (
|
11
|
-
node.type === 'CallExpression' &&
|
12
|
-
(node.callee.name === 'formatMessage' || node.callee.name === '$t') &&
|
13
|
-
node.arguments.length > 0 &&
|
14
|
-
node.arguments[0].properties
|
15
|
-
) {
|
16
|
-
return node.arguments[0].properties.find(
|
17
|
-
a => a.key && a.key.name === attrName
|
18
|
-
);
|
19
|
-
}
|
20
|
-
|
21
|
-
// Find intl.formatMessage usages
|
22
|
-
if (
|
23
|
-
node.type === 'CallExpression' &&
|
24
|
-
node.callee.type === 'MemberExpression' &&
|
25
|
-
(node.callee.object.name === 'intl' ||
|
26
|
-
(node.callee.object.name && node.callee.object.name.endsWith('Intl'))) &&
|
27
|
-
(node.callee.property.name === 'formatMessage' ||
|
28
|
-
node.callee.property.name === '$t')
|
29
|
-
) {
|
30
|
-
return node.arguments[0].properties.find(
|
31
|
-
a => a.key && a.key.name === attrName
|
32
|
-
);
|
33
|
-
}
|
34
|
-
}
|
35
|
-
|
36
|
-
/**
|
37
|
-
* Finds an attribute in FormattedMessage using attribute name.
|
38
|
-
*
|
39
|
-
* @param {Object} node - parent FormattedMessage node
|
40
|
-
* @param {string} attrName - attribute name.
|
41
|
-
* @returns {Object} node - returns node if it finds the attribute.
|
42
|
-
*/
|
43
|
-
function findFormattedMessageAttrNode(node, attrName) {
|
44
|
-
if (
|
45
|
-
node.type === 'JSXIdentifier' &&
|
46
|
-
node.name === 'FormattedMessage' &&
|
47
|
-
node.parent &&
|
48
|
-
node.parent.type === 'JSXOpeningElement'
|
49
|
-
) {
|
50
|
-
return node.parent.attributes.find(a => a.name && a.name.name === attrName);
|
51
|
-
}
|
52
|
-
}
|
53
|
-
|
54
|
-
/**
|
55
|
-
* Finds an attribute in defineMessages using attribute name.
|
56
|
-
*
|
57
|
-
* @param {Object} node - parent defineMessages node
|
58
|
-
* @param {string} attrName - attribute name.
|
59
|
-
* @returns {Object} node - returns node if it finds the attribute.
|
60
|
-
*/
|
61
|
-
function findAttrNodeInDefineMessages(node, attrName) {
|
62
|
-
if (
|
63
|
-
node.type === 'Property' &&
|
64
|
-
node.key.name === attrName &&
|
65
|
-
node.parent &&
|
66
|
-
node.parent.parent &&
|
67
|
-
node.parent.parent.parent &&
|
68
|
-
node.parent.parent.parent.parent &&
|
69
|
-
node.parent.parent.parent.parent.type === 'CallExpression' &&
|
70
|
-
node.parent.parent.parent.parent.callee.name === 'defineMessages'
|
71
|
-
) {
|
72
|
-
return node;
|
73
|
-
}
|
74
|
-
}
|
75
|
-
|
76
|
-
/**
|
77
|
-
* Finds an attribute in defineMessages using attribute name.
|
78
|
-
*
|
79
|
-
* @param {Object} node - parent defineMessages node
|
80
|
-
* @param {string} attrName - attribute name.
|
81
|
-
* @returns {Object} node - returns node if it finds the attribute.
|
82
|
-
*/
|
83
|
-
function findAttrNodeInDefineMessage(node, attrName) {
|
84
|
-
if (
|
85
|
-
node.type === 'Property' &&
|
86
|
-
node.key.name === attrName &&
|
87
|
-
node.parent &&
|
88
|
-
node.parent.parent &&
|
89
|
-
node.parent.parent.type === 'CallExpression' &&
|
90
|
-
node.parent.parent.callee.name === 'defineMessage'
|
91
|
-
) {
|
92
|
-
return node;
|
93
|
-
}
|
94
|
-
}
|
95
|
-
|
96
|
-
/**
|
97
|
-
* Returns a sorted array of nodes, based on their starting posting in the locale id.
|
98
|
-
*
|
99
|
-
* @param {Object} node - parent node containing the locale id.
|
100
|
-
* @returns {Array} child nodes - sorted list.
|
101
|
-
*/
|
102
|
-
function sortedTemplateElements(node) {
|
103
|
-
return [...node.quasis, ...node.expressions].sort(
|
104
|
-
(a, b) => a.range[0] - b.range[0]
|
105
|
-
);
|
106
|
-
}
|
107
|
-
|
108
|
-
/**
|
109
|
-
* Replaces place holders with asterisk and returns the resulting id.
|
110
|
-
*
|
111
|
-
* @param {Object} node - parent node containing the locale id.
|
112
|
-
* @returns {string} id - fixed id.
|
113
|
-
*/
|
114
|
-
function templateLiteralDisplayStr(node) {
|
115
|
-
return sortedTemplateElements(node)
|
116
|
-
.map(e => (!e.value ? '*' : e.value.raw))
|
117
|
-
.join('');
|
118
|
-
}
|
119
|
-
|
120
|
-
module.exports = {
|
121
|
-
findFormatMessageAttrNode,
|
122
|
-
findFormattedMessageAttrNode,
|
123
|
-
findAttrNodeInDefineMessages,
|
124
|
-
findAttrNodeInDefineMessage,
|
125
|
-
sortedTemplateElements,
|
126
|
-
templateLiteralDisplayStr
|
127
|
-
};
|
1
|
+
/**
|
2
|
+
* Finds an attribute in formatMessage using attribute name.
|
3
|
+
*
|
4
|
+
* @param {Object} node - parent formatMessage node
|
5
|
+
* @param {string} attrName - attribute name.
|
6
|
+
* @returns {Object} node - returns node if it finds the attribute.
|
7
|
+
*/
|
8
|
+
function findFormatMessageAttrNode(node, attrName) {
|
9
|
+
// Find formatMessage usages
|
10
|
+
if (
|
11
|
+
node.type === 'CallExpression' &&
|
12
|
+
(node.callee.name === 'formatMessage' || node.callee.name === '$t') &&
|
13
|
+
node.arguments.length > 0 &&
|
14
|
+
node.arguments[0].properties
|
15
|
+
) {
|
16
|
+
return node.arguments[0].properties.find(
|
17
|
+
a => a.key && a.key.name === attrName
|
18
|
+
);
|
19
|
+
}
|
20
|
+
|
21
|
+
// Find intl.formatMessage usages
|
22
|
+
if (
|
23
|
+
node.type === 'CallExpression' &&
|
24
|
+
node.callee.type === 'MemberExpression' &&
|
25
|
+
(node.callee.object.name === 'intl' ||
|
26
|
+
(node.callee.object.name && node.callee.object.name.endsWith('Intl'))) &&
|
27
|
+
(node.callee.property.name === 'formatMessage' ||
|
28
|
+
node.callee.property.name === '$t')
|
29
|
+
) {
|
30
|
+
return node.arguments[0].properties.find(
|
31
|
+
a => a.key && a.key.name === attrName
|
32
|
+
);
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
/**
|
37
|
+
* Finds an attribute in FormattedMessage using attribute name.
|
38
|
+
*
|
39
|
+
* @param {Object} node - parent FormattedMessage node
|
40
|
+
* @param {string} attrName - attribute name.
|
41
|
+
* @returns {Object} node - returns node if it finds the attribute.
|
42
|
+
*/
|
43
|
+
function findFormattedMessageAttrNode(node, attrName) {
|
44
|
+
if (
|
45
|
+
node.type === 'JSXIdentifier' &&
|
46
|
+
node.name === 'FormattedMessage' &&
|
47
|
+
node.parent &&
|
48
|
+
node.parent.type === 'JSXOpeningElement'
|
49
|
+
) {
|
50
|
+
return node.parent.attributes.find(a => a.name && a.name.name === attrName);
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
/**
|
55
|
+
* Finds an attribute in defineMessages using attribute name.
|
56
|
+
*
|
57
|
+
* @param {Object} node - parent defineMessages node
|
58
|
+
* @param {string} attrName - attribute name.
|
59
|
+
* @returns {Object} node - returns node if it finds the attribute.
|
60
|
+
*/
|
61
|
+
function findAttrNodeInDefineMessages(node, attrName) {
|
62
|
+
if (
|
63
|
+
node.type === 'Property' &&
|
64
|
+
node.key.name === attrName &&
|
65
|
+
node.parent &&
|
66
|
+
node.parent.parent &&
|
67
|
+
node.parent.parent.parent &&
|
68
|
+
node.parent.parent.parent.parent &&
|
69
|
+
node.parent.parent.parent.parent.type === 'CallExpression' &&
|
70
|
+
node.parent.parent.parent.parent.callee.name === 'defineMessages'
|
71
|
+
) {
|
72
|
+
return node;
|
73
|
+
}
|
74
|
+
}
|
75
|
+
|
76
|
+
/**
|
77
|
+
* Finds an attribute in defineMessages using attribute name.
|
78
|
+
*
|
79
|
+
* @param {Object} node - parent defineMessages node
|
80
|
+
* @param {string} attrName - attribute name.
|
81
|
+
* @returns {Object} node - returns node if it finds the attribute.
|
82
|
+
*/
|
83
|
+
function findAttrNodeInDefineMessage(node, attrName) {
|
84
|
+
if (
|
85
|
+
node.type === 'Property' &&
|
86
|
+
node.key.name === attrName &&
|
87
|
+
node.parent &&
|
88
|
+
node.parent.parent &&
|
89
|
+
node.parent.parent.type === 'CallExpression' &&
|
90
|
+
node.parent.parent.callee.name === 'defineMessage'
|
91
|
+
) {
|
92
|
+
return node;
|
93
|
+
}
|
94
|
+
}
|
95
|
+
|
96
|
+
/**
|
97
|
+
* Returns a sorted array of nodes, based on their starting posting in the locale id.
|
98
|
+
*
|
99
|
+
* @param {Object} node - parent node containing the locale id.
|
100
|
+
* @returns {Array} child nodes - sorted list.
|
101
|
+
*/
|
102
|
+
function sortedTemplateElements(node) {
|
103
|
+
return [...node.quasis, ...node.expressions].sort(
|
104
|
+
(a, b) => a.range[0] - b.range[0]
|
105
|
+
);
|
106
|
+
}
|
107
|
+
|
108
|
+
/**
|
109
|
+
* Replaces place holders with asterisk and returns the resulting id.
|
110
|
+
*
|
111
|
+
* @param {Object} node - parent node containing the locale id.
|
112
|
+
* @returns {string} id - fixed id.
|
113
|
+
*/
|
114
|
+
function templateLiteralDisplayStr(node) {
|
115
|
+
return sortedTemplateElements(node)
|
116
|
+
.map(e => (!e.value ? '*' : e.value.raw))
|
117
|
+
.join('');
|
118
|
+
}
|
119
|
+
|
120
|
+
module.exports = {
|
121
|
+
findFormatMessageAttrNode,
|
122
|
+
findFormattedMessageAttrNode,
|
123
|
+
findAttrNodeInDefineMessages,
|
124
|
+
findAttrNodeInDefineMessage,
|
125
|
+
sortedTemplateElements,
|
126
|
+
templateLiteralDisplayStr
|
127
|
+
};
|
package/lib/util/settings.js
CHANGED
@@ -1,14 +1,14 @@
|
|
1
|
-
/**
|
2
|
-
* Get a setting from eslint config
|
3
|
-
*
|
4
|
-
* @param {object} context - Context
|
5
|
-
* @param {string} name - Name
|
6
|
-
* @returns {any} result
|
7
|
-
*/
|
8
|
-
function getSetting(context, name) {
|
9
|
-
return context.settings[`agilebot/${name}`];
|
10
|
-
}
|
11
|
-
|
12
|
-
module.exports = {
|
13
|
-
getSetting
|
14
|
-
};
|
1
|
+
/**
|
2
|
+
* Get a setting from eslint config
|
3
|
+
*
|
4
|
+
* @param {object} context - Context
|
5
|
+
* @param {string} name - Name
|
6
|
+
* @returns {any} result
|
7
|
+
*/
|
8
|
+
function getSetting(context, name) {
|
9
|
+
return context.settings[`agilebot/${name}`];
|
10
|
+
}
|
11
|
+
|
12
|
+
module.exports = {
|
13
|
+
getSetting
|
14
|
+
};
|
package/lib/util/translations.js
CHANGED
@@ -1,66 +1,66 @@
|
|
1
|
-
const fs = require('node:fs');
|
2
|
-
const path = require('node:path');
|
3
|
-
const { tsImport } = require('@agilebot/eslint-utils');
|
4
|
-
const { getSetting } = require('./settings');
|
5
|
-
|
6
|
-
/**
|
7
|
-
* Map of locale file paths to keys and modified time
|
8
|
-
*
|
9
|
-
* @type {{string: {keys: Array, mtime: number}}}
|
10
|
-
*/
|
11
|
-
const localeFilesKeys = {};
|
12
|
-
|
13
|
-
/**
|
14
|
-
* Get a list of ids keys from reading locale files
|
15
|
-
* Keeps track of modified times and reloads if changed,; useful for realtime eslint in-editor
|
16
|
-
*
|
17
|
-
* @param {object} context - Context
|
18
|
-
* @returns {string[]} results - Array of ids
|
19
|
-
*/
|
20
|
-
function getIntlIds(context) {
|
21
|
-
const projectRoot = getSetting(context, 'project-root');
|
22
|
-
const localeFiles = getSetting(context, 'locale-files');
|
23
|
-
|
24
|
-
if (!localeFiles) {
|
25
|
-
throw new Error('localeFiles not in settings');
|
26
|
-
}
|
27
|
-
|
28
|
-
const results = [];
|
29
|
-
localeFiles.forEach(f => {
|
30
|
-
const fullPath = projectRoot ? path.join(projectRoot, f) : f;
|
31
|
-
const mtime = fs.lstatSync(fullPath).mtime.getTime();
|
32
|
-
if (
|
33
|
-
!localeFilesKeys[fullPath] ||
|
34
|
-
mtime !== localeFilesKeys[fullPath].mtime
|
35
|
-
) {
|
36
|
-
let json;
|
37
|
-
if (fullPath.endsWith('.json')) {
|
38
|
-
json = JSON.parse(fs.readFileSync(fullPath));
|
39
|
-
} else if (fullPath.endsWith('.ts')) {
|
40
|
-
json = tsImport(fullPath);
|
41
|
-
if (typeof json === 'object' && json.default) {
|
42
|
-
json = json.default;
|
43
|
-
}
|
44
|
-
} else if (fullPath.endsWith('.js')) {
|
45
|
-
json = require(fullPath);
|
46
|
-
if (typeof json === 'object' && json.default) {
|
47
|
-
json = json.default;
|
48
|
-
}
|
49
|
-
} else {
|
50
|
-
throw new Error('unsupported file extension');
|
51
|
-
}
|
52
|
-
|
53
|
-
localeFilesKeys[fullPath] = {
|
54
|
-
keys: Object.keys(json),
|
55
|
-
mtime: mtime
|
56
|
-
};
|
57
|
-
}
|
58
|
-
results.push(...localeFilesKeys[fullPath].keys);
|
59
|
-
});
|
60
|
-
|
61
|
-
return results;
|
62
|
-
}
|
63
|
-
|
64
|
-
module.exports = {
|
65
|
-
getIntlIds
|
66
|
-
};
|
1
|
+
const fs = require('node:fs');
|
2
|
+
const path = require('node:path');
|
3
|
+
const { tsImport } = require('@agilebot/eslint-utils');
|
4
|
+
const { getSetting } = require('./settings');
|
5
|
+
|
6
|
+
/**
|
7
|
+
* Map of locale file paths to keys and modified time
|
8
|
+
*
|
9
|
+
* @type {{string: {keys: Array, mtime: number}}}
|
10
|
+
*/
|
11
|
+
const localeFilesKeys = {};
|
12
|
+
|
13
|
+
/**
|
14
|
+
* Get a list of ids keys from reading locale files
|
15
|
+
* Keeps track of modified times and reloads if changed,; useful for realtime eslint in-editor
|
16
|
+
*
|
17
|
+
* @param {object} context - Context
|
18
|
+
* @returns {string[]} results - Array of ids
|
19
|
+
*/
|
20
|
+
function getIntlIds(context) {
|
21
|
+
const projectRoot = getSetting(context, 'project-root');
|
22
|
+
const localeFiles = getSetting(context, 'locale-files');
|
23
|
+
|
24
|
+
if (!localeFiles) {
|
25
|
+
throw new Error('localeFiles not in settings');
|
26
|
+
}
|
27
|
+
|
28
|
+
const results = [];
|
29
|
+
localeFiles.forEach(f => {
|
30
|
+
const fullPath = projectRoot ? path.join(projectRoot, f) : f;
|
31
|
+
const mtime = fs.lstatSync(fullPath).mtime.getTime();
|
32
|
+
if (
|
33
|
+
!localeFilesKeys[fullPath] ||
|
34
|
+
mtime !== localeFilesKeys[fullPath].mtime
|
35
|
+
) {
|
36
|
+
let json;
|
37
|
+
if (fullPath.endsWith('.json')) {
|
38
|
+
json = JSON.parse(fs.readFileSync(fullPath));
|
39
|
+
} else if (fullPath.endsWith('.ts')) {
|
40
|
+
json = tsImport(fullPath);
|
41
|
+
if (typeof json === 'object' && json.default) {
|
42
|
+
json = json.default;
|
43
|
+
}
|
44
|
+
} else if (fullPath.endsWith('.js')) {
|
45
|
+
json = require(fullPath);
|
46
|
+
if (typeof json === 'object' && json.default) {
|
47
|
+
json = json.default;
|
48
|
+
}
|
49
|
+
} else {
|
50
|
+
throw new Error('unsupported file extension');
|
51
|
+
}
|
52
|
+
|
53
|
+
localeFilesKeys[fullPath] = {
|
54
|
+
keys: Object.keys(json),
|
55
|
+
mtime: mtime
|
56
|
+
};
|
57
|
+
}
|
58
|
+
results.push(...localeFilesKeys[fullPath].keys);
|
59
|
+
});
|
60
|
+
|
61
|
+
return results;
|
62
|
+
}
|
63
|
+
|
64
|
+
module.exports = {
|
65
|
+
getIntlIds
|
66
|
+
};
|