@nkzw/eslint-plugin-fbtee 1.7.0 → 1.7.3
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 +2 -1
- package/lib/{index.d.ts → index.d.mts} +9 -3
- package/lib/{index.js → index.mjs} +16 -54
- package/package.json +6 -7
- package/lib/index.cjs +0 -382
- package/lib/index.d.cts +0 -32
package/README.md
CHANGED
|
@@ -114,7 +114,8 @@ export default {
|
|
|
114
114
|
By default, **fbtee** expects your source code to be anywhere within your project, your translations in a `translations` folder, and the generated translations in `src/translations`. You can customize these paths using command line arguments:
|
|
115
115
|
|
|
116
116
|
- The `--translations` parameter can be specified to `fbtee translate` to customize the path to the input translation files.
|
|
117
|
-
- `--output-dir`
|
|
117
|
+
- The `--output-dir` parameter defines where the output translation files per language should be written (one file per locale), so they can be lazy loaded in your app.
|
|
118
|
+
- The `--output-file` parameter outputs all translations in a single combined file, which can be used directly in your app without loading individual files.
|
|
118
119
|
|
|
119
120
|
If you want to use different paths, it is recommended to define custom commands in your `package.json`:
|
|
120
121
|
|
|
@@ -22,11 +22,17 @@ declare const _default: {
|
|
|
22
22
|
version: string;
|
|
23
23
|
};
|
|
24
24
|
rules: {
|
|
25
|
-
'no-empty-strings': _typescript_eslint_utils_ts_eslint0.RuleModule<"emptyString" | "jsxEmptyString", [], unknown, _typescript_eslint_utils_ts_eslint0.RuleListener
|
|
26
|
-
|
|
25
|
+
'no-empty-strings': _typescript_eslint_utils_ts_eslint0.RuleModule<"emptyString" | "jsxEmptyString", [], unknown, _typescript_eslint_utils_ts_eslint0.RuleListener> & {
|
|
26
|
+
name: string;
|
|
27
|
+
};
|
|
28
|
+
'no-unhelpful-desc': _typescript_eslint_utils_ts_eslint0.RuleModule<"emptyDesc" | "jsxEmptyDesc" | "duplicateDesc" | "shortDesc", [], unknown, _typescript_eslint_utils_ts_eslint0.RuleListener> & {
|
|
29
|
+
name: string;
|
|
30
|
+
};
|
|
27
31
|
'no-untranslated-strings': _typescript_eslint_utils_ts_eslint0.RuleModule<"unwrappedString", [{
|
|
28
32
|
ignoredWords: Array<string>;
|
|
29
|
-
}], unknown, _typescript_eslint_utils_ts_eslint0.RuleListener
|
|
33
|
+
}], unknown, _typescript_eslint_utils_ts_eslint0.RuleListener> & {
|
|
34
|
+
name: string;
|
|
35
|
+
};
|
|
30
36
|
};
|
|
31
37
|
};
|
|
32
38
|
//#endregion
|
|
@@ -1,57 +1,24 @@
|
|
|
1
1
|
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
2
|
-
|
|
3
2
|
//#region package.json
|
|
4
3
|
var name = "@nkzw/eslint-plugin-fbtee";
|
|
5
|
-
var version = "1.7.
|
|
6
|
-
var description = "";
|
|
7
|
-
var keywords = [];
|
|
8
|
-
var repository = {
|
|
9
|
-
"type": "git",
|
|
10
|
-
"url": "git+https://github.com/nkzw-tech/fbtee.git",
|
|
11
|
-
"directory": "packages/eslint-plugin-fbtee"
|
|
12
|
-
};
|
|
13
|
-
var license = "MIT";
|
|
14
|
-
var type = "module";
|
|
15
|
-
var exports = {
|
|
16
|
-
"import": "./lib/index.js",
|
|
17
|
-
"require": "./lib/index.cjs"
|
|
18
|
-
};
|
|
19
|
-
var main = "lib/index.cjs";
|
|
20
|
-
var scripts = { "build": "tsdown src/index.tsx -d lib --target=node22 --format=esm,cjs --clean --dts" };
|
|
21
|
-
var dependencies = { "@typescript-eslint/utils": "^8.46.0" };
|
|
22
|
-
var devDependencies = { "@typescript-eslint/rule-tester": "^8.46.0" };
|
|
23
|
-
var package_default = {
|
|
24
|
-
name,
|
|
25
|
-
version,
|
|
26
|
-
description,
|
|
27
|
-
keywords,
|
|
28
|
-
repository,
|
|
29
|
-
license,
|
|
30
|
-
type,
|
|
31
|
-
exports,
|
|
32
|
-
main,
|
|
33
|
-
scripts,
|
|
34
|
-
dependencies,
|
|
35
|
-
devDependencies
|
|
36
|
-
};
|
|
37
|
-
|
|
4
|
+
var version = "1.7.3";
|
|
38
5
|
//#endregion
|
|
39
6
|
//#region src/utils.tsx
|
|
40
|
-
const createRule = ESLintUtils.RuleCreator((name
|
|
7
|
+
const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/nkzw-tech/fbtee/blob/main/packages/eslint-plugin-fbtee/docs/rules/${name}.md`);
|
|
41
8
|
function resolveMemberExpressions(object, property) {
|
|
42
9
|
if (object.type === "JSXMemberExpression") return `${resolveMemberExpressions(object.object, object.property)}.${property.name}`;
|
|
43
10
|
return `${object.name}.${property.name}`;
|
|
44
11
|
}
|
|
45
12
|
function elementType(node) {
|
|
46
13
|
if (node.type === "JSXFragment") return "<>";
|
|
47
|
-
const { name
|
|
48
|
-
switch (name
|
|
49
|
-
case "JSXIdentifier": return name
|
|
14
|
+
const { name } = node.openingElement;
|
|
15
|
+
switch (name.type) {
|
|
16
|
+
case "JSXIdentifier": return name.name;
|
|
50
17
|
case "JSXMemberExpression": {
|
|
51
|
-
const { object, property } = name
|
|
18
|
+
const { object, property } = name;
|
|
52
19
|
return resolveMemberExpressions(object, property);
|
|
53
20
|
}
|
|
54
|
-
case "JSXNamespacedName": return `${name
|
|
21
|
+
case "JSXNamespacedName": return `${name.namespace.name}:${name.name.name}`;
|
|
55
22
|
default: throw new Error("Unsupported JSX node type.");
|
|
56
23
|
}
|
|
57
24
|
}
|
|
@@ -101,20 +68,19 @@ function hasFbtParent(node) {
|
|
|
101
68
|
function isFbtNode(node) {
|
|
102
69
|
if (!node) return null;
|
|
103
70
|
if (node.type === "JSXElement") {
|
|
104
|
-
const name
|
|
105
|
-
return name
|
|
71
|
+
const name = elementType(node);
|
|
72
|
+
return name === "fbt" || name === "fbs" || name?.startsWith("fbt:");
|
|
106
73
|
}
|
|
107
74
|
if (node.type === "CallExpression" && node.callee.type === "Identifier") {
|
|
108
|
-
const name
|
|
109
|
-
return name
|
|
75
|
+
const name = node.callee.name;
|
|
76
|
+
return name === "fbt" || name === "fbs";
|
|
110
77
|
}
|
|
111
78
|
if (node.type === "MemberExpression" && node.object.type === "CallExpression" && node.object.callee.type === "Identifier") {
|
|
112
|
-
const name
|
|
113
|
-
return name
|
|
79
|
+
const name = node.object.callee.name;
|
|
80
|
+
return name === "fbt" || name === "fbs";
|
|
114
81
|
}
|
|
115
82
|
return false;
|
|
116
83
|
}
|
|
117
|
-
|
|
118
84
|
//#endregion
|
|
119
85
|
//#region src/rules/no-empty-strings.tsx
|
|
120
86
|
var no_empty_strings_default = createRule({
|
|
@@ -177,7 +143,6 @@ function validateChildren(node) {
|
|
|
177
143
|
function isEmptyString(node) {
|
|
178
144
|
return resolveNodeValue(node)?.trim() === "";
|
|
179
145
|
}
|
|
180
|
-
|
|
181
146
|
//#endregion
|
|
182
147
|
//#region src/rules/no-unhelpful-desc.tsx
|
|
183
148
|
var no_unhelpful_desc_default = createRule({
|
|
@@ -248,7 +213,6 @@ var no_unhelpful_desc_default = createRule({
|
|
|
248
213
|
},
|
|
249
214
|
name: "no-unhelpful-desc"
|
|
250
215
|
});
|
|
251
|
-
|
|
252
216
|
//#endregion
|
|
253
217
|
//#region src/rules/no-untranslated-strings.tsx
|
|
254
218
|
const attributes = new Set([
|
|
@@ -328,7 +292,6 @@ var no_untranslated_strings_default = createRule({
|
|
|
328
292
|
},
|
|
329
293
|
name: "no-untranslated-strings"
|
|
330
294
|
});
|
|
331
|
-
|
|
332
295
|
//#endregion
|
|
333
296
|
//#region src/index.tsx
|
|
334
297
|
var src_default = {
|
|
@@ -344,8 +307,8 @@ var src_default = {
|
|
|
344
307
|
} }
|
|
345
308
|
},
|
|
346
309
|
meta: {
|
|
347
|
-
name
|
|
348
|
-
version
|
|
310
|
+
name,
|
|
311
|
+
version
|
|
349
312
|
},
|
|
350
313
|
rules: {
|
|
351
314
|
"no-empty-strings": no_empty_strings_default,
|
|
@@ -353,6 +316,5 @@ var src_default = {
|
|
|
353
316
|
"no-untranslated-strings": no_untranslated_strings_default
|
|
354
317
|
}
|
|
355
318
|
};
|
|
356
|
-
|
|
357
319
|
//#endregion
|
|
358
|
-
export { src_default as default };
|
|
320
|
+
export { src_default as default };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nkzw/eslint-plugin-fbtee",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.3",
|
|
4
4
|
"description": "",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"repository": {
|
|
@@ -11,17 +11,16 @@
|
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"type": "module",
|
|
13
13
|
"exports": {
|
|
14
|
-
"import": "./lib/index.
|
|
15
|
-
"require": "./lib/index.cjs"
|
|
14
|
+
"import": "./lib/index.mjs"
|
|
16
15
|
},
|
|
17
|
-
"main": "lib/index.
|
|
16
|
+
"main": "lib/index.mjs",
|
|
18
17
|
"dependencies": {
|
|
19
|
-
"@typescript-eslint/utils": "^8.
|
|
18
|
+
"@typescript-eslint/utils": "^8.56.1"
|
|
20
19
|
},
|
|
21
20
|
"devDependencies": {
|
|
22
|
-
"@typescript-eslint/rule-tester": "^8.
|
|
21
|
+
"@typescript-eslint/rule-tester": "^8.56.1"
|
|
23
22
|
},
|
|
24
23
|
"scripts": {
|
|
25
|
-
"build": "tsdown src/index.tsx -d lib --target=node22 --format=esm
|
|
24
|
+
"build": "tsdown src/index.tsx -d lib --target=node22 --format=esm --clean --dts"
|
|
26
25
|
}
|
|
27
26
|
}
|
package/lib/index.cjs
DELETED
|
@@ -1,382 +0,0 @@
|
|
|
1
|
-
//#region rolldown:runtime
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __copyProps = (to, from, except, desc) => {
|
|
9
|
-
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
10
|
-
key = keys[i];
|
|
11
|
-
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
12
|
-
get: ((k) => from[k]).bind(null, key),
|
|
13
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
19
|
-
value: mod,
|
|
20
|
-
enumerable: true
|
|
21
|
-
}) : target, mod));
|
|
22
|
-
|
|
23
|
-
//#endregion
|
|
24
|
-
let __typescript_eslint_utils = require("@typescript-eslint/utils");
|
|
25
|
-
__typescript_eslint_utils = __toESM(__typescript_eslint_utils);
|
|
26
|
-
|
|
27
|
-
//#region package.json
|
|
28
|
-
var name = "@nkzw/eslint-plugin-fbtee";
|
|
29
|
-
var version = "1.7.0";
|
|
30
|
-
var description = "";
|
|
31
|
-
var keywords = [];
|
|
32
|
-
var repository = {
|
|
33
|
-
"type": "git",
|
|
34
|
-
"url": "git+https://github.com/nkzw-tech/fbtee.git",
|
|
35
|
-
"directory": "packages/eslint-plugin-fbtee"
|
|
36
|
-
};
|
|
37
|
-
var license = "MIT";
|
|
38
|
-
var type = "module";
|
|
39
|
-
var exports$1 = {
|
|
40
|
-
"import": "./lib/index.js",
|
|
41
|
-
"require": "./lib/index.cjs"
|
|
42
|
-
};
|
|
43
|
-
var main = "lib/index.cjs";
|
|
44
|
-
var scripts = { "build": "tsdown src/index.tsx -d lib --target=node22 --format=esm,cjs --clean --dts" };
|
|
45
|
-
var dependencies = { "@typescript-eslint/utils": "^8.46.0" };
|
|
46
|
-
var devDependencies = { "@typescript-eslint/rule-tester": "^8.46.0" };
|
|
47
|
-
var package_default = {
|
|
48
|
-
name,
|
|
49
|
-
version,
|
|
50
|
-
description,
|
|
51
|
-
keywords,
|
|
52
|
-
repository,
|
|
53
|
-
license,
|
|
54
|
-
type,
|
|
55
|
-
exports: exports$1,
|
|
56
|
-
main,
|
|
57
|
-
scripts,
|
|
58
|
-
dependencies,
|
|
59
|
-
devDependencies
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
//#endregion
|
|
63
|
-
//#region src/utils.tsx
|
|
64
|
-
const createRule = __typescript_eslint_utils.ESLintUtils.RuleCreator((name$1) => `https://github.com/nkzw-tech/fbtee/blob/main/packages/eslint-plugin-fbtee/docs/rules/${name$1}.md`);
|
|
65
|
-
function resolveMemberExpressions(object, property) {
|
|
66
|
-
if (object.type === "JSXMemberExpression") return `${resolveMemberExpressions(object.object, object.property)}.${property.name}`;
|
|
67
|
-
return `${object.name}.${property.name}`;
|
|
68
|
-
}
|
|
69
|
-
function elementType(node) {
|
|
70
|
-
if (node.type === "JSXFragment") return "<>";
|
|
71
|
-
const { name: name$1 } = node.openingElement;
|
|
72
|
-
switch (name$1.type) {
|
|
73
|
-
case "JSXIdentifier": return name$1.name;
|
|
74
|
-
case "JSXMemberExpression": {
|
|
75
|
-
const { object, property } = name$1;
|
|
76
|
-
return resolveMemberExpressions(object, property);
|
|
77
|
-
}
|
|
78
|
-
case "JSXNamespacedName": return `${name$1.namespace.name}:${name$1.name.name}`;
|
|
79
|
-
default: throw new Error("Unsupported JSX node type.");
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
function resolveJsxElementTextContent(node) {
|
|
83
|
-
return node.children.map((child) => {
|
|
84
|
-
if (child.type === "JSXText") return child.value.trim();
|
|
85
|
-
if (child.type === "JSXExpressionContainer") return resolveNodeValue(child.expression) || "";
|
|
86
|
-
return "";
|
|
87
|
-
}).join("").trim();
|
|
88
|
-
}
|
|
89
|
-
function resolveBinaryExpression(node) {
|
|
90
|
-
if (node.operator !== "+") return null;
|
|
91
|
-
const leftValue = resolveNodeValue(node.left);
|
|
92
|
-
const rightValue = resolveNodeValue(node.right);
|
|
93
|
-
if (leftValue !== null && rightValue !== null) return leftValue + rightValue;
|
|
94
|
-
return null;
|
|
95
|
-
}
|
|
96
|
-
function resolveNodeValue(node) {
|
|
97
|
-
if (node.type === "Literal" && typeof node.value === "string") return node.value;
|
|
98
|
-
if (node.type === "TemplateLiteral" && node.expressions.length === 0) return node.quasis.map((quasi) => quasi.value.raw).join("");
|
|
99
|
-
if (node.type === "BinaryExpression") return resolveBinaryExpression(node);
|
|
100
|
-
if (node.type === "JSXExpressionContainer") return resolveNodeValue(node.expression);
|
|
101
|
-
if (node.type === "ConditionalExpression") {
|
|
102
|
-
const consequent = resolveNodeValue(node.consequent);
|
|
103
|
-
const alternate = resolveNodeValue(node.alternate);
|
|
104
|
-
return consequent || alternate || null;
|
|
105
|
-
}
|
|
106
|
-
if (node.type === "LogicalExpression") {
|
|
107
|
-
const left = resolveNodeValue(node.left);
|
|
108
|
-
const right = resolveNodeValue(node.right);
|
|
109
|
-
return left || right || null;
|
|
110
|
-
}
|
|
111
|
-
return null;
|
|
112
|
-
}
|
|
113
|
-
function getPropName(prop) {
|
|
114
|
-
if (prop.name.type === "JSXNamespacedName") return `${prop.name.namespace.name}:${prop.name.name.name}`;
|
|
115
|
-
return prop.name.name;
|
|
116
|
-
}
|
|
117
|
-
function hasFbtParent(node) {
|
|
118
|
-
let current = node.parent;
|
|
119
|
-
while (current) {
|
|
120
|
-
if (isFbtNode(current)) return true;
|
|
121
|
-
current = current.parent;
|
|
122
|
-
}
|
|
123
|
-
return false;
|
|
124
|
-
}
|
|
125
|
-
function isFbtNode(node) {
|
|
126
|
-
if (!node) return null;
|
|
127
|
-
if (node.type === "JSXElement") {
|
|
128
|
-
const name$1 = elementType(node);
|
|
129
|
-
return name$1 === "fbt" || name$1 === "fbs" || name$1?.startsWith("fbt:");
|
|
130
|
-
}
|
|
131
|
-
if (node.type === "CallExpression" && node.callee.type === "Identifier") {
|
|
132
|
-
const name$1 = node.callee.name;
|
|
133
|
-
return name$1 === "fbt" || name$1 === "fbs";
|
|
134
|
-
}
|
|
135
|
-
if (node.type === "MemberExpression" && node.object.type === "CallExpression" && node.object.callee.type === "Identifier") {
|
|
136
|
-
const name$1 = node.object.callee.name;
|
|
137
|
-
return name$1 === "fbt" || name$1 === "fbs";
|
|
138
|
-
}
|
|
139
|
-
return false;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
//#endregion
|
|
143
|
-
//#region src/rules/no-empty-strings.tsx
|
|
144
|
-
var no_empty_strings_default = createRule({
|
|
145
|
-
create(context) {
|
|
146
|
-
return {
|
|
147
|
-
CallExpression(node) {
|
|
148
|
-
if (!isFbtNode(node)) return;
|
|
149
|
-
const [text] = node.arguments;
|
|
150
|
-
if (text && text.type !== "SpreadElement" && isEmptyString(text)) context.report({
|
|
151
|
-
messageId: "emptyString",
|
|
152
|
-
node: text
|
|
153
|
-
});
|
|
154
|
-
},
|
|
155
|
-
JSXElement(node) {
|
|
156
|
-
if (!isFbtNode(node)) return;
|
|
157
|
-
if (node.openingElement.name.type === "JSXNamespacedName") return;
|
|
158
|
-
if (!validateChildren(node)) context.report({
|
|
159
|
-
messageId: "jsxEmptyString",
|
|
160
|
-
node
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
};
|
|
164
|
-
},
|
|
165
|
-
defaultOptions: [],
|
|
166
|
-
meta: {
|
|
167
|
-
docs: { description: "Disallow empty strings in fbt elements or function calls." },
|
|
168
|
-
messages: {
|
|
169
|
-
emptyString: "Empty strings are not allowed in fbt() and fbs() function arguments.",
|
|
170
|
-
jsxEmptyString: "Empty strings are not allowed as children of `<fbt>` and `<fbs>` tags."
|
|
171
|
-
},
|
|
172
|
-
schema: [],
|
|
173
|
-
type: "problem"
|
|
174
|
-
},
|
|
175
|
-
name: "no-empty-strings"
|
|
176
|
-
});
|
|
177
|
-
function validateChildren(node) {
|
|
178
|
-
let hasTextContent = false;
|
|
179
|
-
for (const child of node.children) {
|
|
180
|
-
if (child.type === "JSXText" && child.value.trim() !== "") hasTextContent = true;
|
|
181
|
-
if (child.type === "JSXExpressionContainer") {
|
|
182
|
-
if (child.expression.type === "JSXEmptyExpression") continue;
|
|
183
|
-
if (child.expression.type === "Identifier" && child.expression.name !== "undefined") {
|
|
184
|
-
hasTextContent = true;
|
|
185
|
-
continue;
|
|
186
|
-
}
|
|
187
|
-
if (resolveNodeValue(child.expression)?.trim()) hasTextContent = true;
|
|
188
|
-
}
|
|
189
|
-
if (child.type === "JSXElement" || child.type === "JSXFragment") {
|
|
190
|
-
const childName = elementType(child);
|
|
191
|
-
if (/^[A-Z]/.test(childName) || childName.startsWith("fbt:")) {
|
|
192
|
-
hasTextContent = true;
|
|
193
|
-
continue;
|
|
194
|
-
}
|
|
195
|
-
hasTextContent ||= validateChildren(child);
|
|
196
|
-
}
|
|
197
|
-
if (hasTextContent) break;
|
|
198
|
-
}
|
|
199
|
-
return hasTextContent;
|
|
200
|
-
}
|
|
201
|
-
function isEmptyString(node) {
|
|
202
|
-
return resolveNodeValue(node)?.trim() === "";
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
//#endregion
|
|
206
|
-
//#region src/rules/no-unhelpful-desc.tsx
|
|
207
|
-
var no_unhelpful_desc_default = createRule({
|
|
208
|
-
create(context) {
|
|
209
|
-
return {
|
|
210
|
-
CallExpression(node) {
|
|
211
|
-
if (!isFbtNode(node)) return;
|
|
212
|
-
const [textArg, descArg] = node.arguments;
|
|
213
|
-
const desc = descArg && descArg.type !== "SpreadElement" ? resolveNodeValue(descArg)?.trim() : null;
|
|
214
|
-
if (!desc) {
|
|
215
|
-
context.report({
|
|
216
|
-
messageId: "emptyDesc",
|
|
217
|
-
node: descArg || node
|
|
218
|
-
});
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
|
-
if (desc.length <= 4) {
|
|
222
|
-
context.report({
|
|
223
|
-
messageId: "shortDesc",
|
|
224
|
-
node: descArg || node
|
|
225
|
-
});
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
|
-
const text = textArg && textArg.type !== "SpreadElement" ? resolveNodeValue(textArg)?.trim() : null;
|
|
229
|
-
if (desc.toLowerCase() === text?.toLowerCase()) context.report({
|
|
230
|
-
messageId: "duplicateDesc",
|
|
231
|
-
node: descArg || node
|
|
232
|
-
});
|
|
233
|
-
},
|
|
234
|
-
JSXAttribute(node) {
|
|
235
|
-
if (!isFbtNode(node.parent.parent)) return;
|
|
236
|
-
if (node.name.name !== "desc" || !node.value) return;
|
|
237
|
-
if (node.value.type === "JSXExpressionContainer" && (node.value.expression.type === "Identifier" || node.value.expression.type === "CallExpression")) return;
|
|
238
|
-
const desc = resolveNodeValue(node.value)?.trim();
|
|
239
|
-
if (!desc) {
|
|
240
|
-
context.report({
|
|
241
|
-
messageId: "jsxEmptyDesc",
|
|
242
|
-
node: node.value
|
|
243
|
-
});
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
if (desc.length <= 4) {
|
|
247
|
-
context.report({
|
|
248
|
-
messageId: "shortDesc",
|
|
249
|
-
node: node.value
|
|
250
|
-
});
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
const textContent = resolveJsxElementTextContent(node.parent.parent);
|
|
254
|
-
if (desc.toLowerCase() === textContent.toLowerCase()) context.report({
|
|
255
|
-
messageId: "duplicateDesc",
|
|
256
|
-
node: node.value
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
};
|
|
260
|
-
},
|
|
261
|
-
defaultOptions: [],
|
|
262
|
-
meta: {
|
|
263
|
-
docs: { description: "Enforce meaningful and non-empty descriptions in <fbt> elements and fbt()/fbs() function calls." },
|
|
264
|
-
messages: {
|
|
265
|
-
duplicateDesc: "The \"desc\" attribute or argument should not be a duplicate of the text content.",
|
|
266
|
-
emptyDesc: "The \"desc\" argument in fbt() and fbs() must not be empty.",
|
|
267
|
-
jsxEmptyDesc: "The \"desc\" attribute in <fbt> must not be empty.",
|
|
268
|
-
shortDesc: "The \"desc\" attribute or argument is too short. Use a more descriptive and meaningful explanation."
|
|
269
|
-
},
|
|
270
|
-
schema: [],
|
|
271
|
-
type: "problem"
|
|
272
|
-
},
|
|
273
|
-
name: "no-unhelpful-desc"
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
//#endregion
|
|
277
|
-
//#region src/rules/no-untranslated-strings.tsx
|
|
278
|
-
const attributes = new Set([
|
|
279
|
-
"title",
|
|
280
|
-
"placeholder",
|
|
281
|
-
"alt",
|
|
282
|
-
"label",
|
|
283
|
-
"aria-label",
|
|
284
|
-
"aria-errormessage"
|
|
285
|
-
]);
|
|
286
|
-
const shouldIgnoreParent = (node) => {
|
|
287
|
-
const { parent } = node;
|
|
288
|
-
const openingElement = parent?.type === "JSXElement" ? parent?.openingElement : null;
|
|
289
|
-
return openingElement?.type === "JSXOpeningElement" && openingElement.name.type === "JSXIdentifier" && (openingElement.name.name === "pre" || openingElement.name.name === "code");
|
|
290
|
-
};
|
|
291
|
-
var no_untranslated_strings_default = createRule({
|
|
292
|
-
create(context, options) {
|
|
293
|
-
const ignoredWords = new Set(options[0].ignoredWords.map((value) => value.trim().toLowerCase()));
|
|
294
|
-
const isIgnoredWord = (value) => ignoredWords.has(value.replaceAll(/\s+/g, " ").toLowerCase());
|
|
295
|
-
return {
|
|
296
|
-
JSXAttribute(node) {
|
|
297
|
-
const propName = getPropName(node);
|
|
298
|
-
if (!attributes.has(propName)) return;
|
|
299
|
-
const value = node.value ? resolveNodeValue(node.value) : null;
|
|
300
|
-
if (!value) return;
|
|
301
|
-
if (isIgnoredWord(value)) return;
|
|
302
|
-
if (node.value?.type === "JSXExpressionContainer" && isFbtNode(node.value.expression)) return;
|
|
303
|
-
context.report({
|
|
304
|
-
messageId: "unwrappedString",
|
|
305
|
-
node
|
|
306
|
-
});
|
|
307
|
-
},
|
|
308
|
-
JSXExpressionContainer(node) {
|
|
309
|
-
if (node.parent.type === "JSXAttribute") return;
|
|
310
|
-
const value = resolveNodeValue(node.expression);
|
|
311
|
-
if (!value) return;
|
|
312
|
-
if (isIgnoredWord(value)) return;
|
|
313
|
-
if (shouldIgnoreParent(node)) return;
|
|
314
|
-
if (!hasFbtParent(node)) context.report({
|
|
315
|
-
messageId: "unwrappedString",
|
|
316
|
-
node
|
|
317
|
-
});
|
|
318
|
-
},
|
|
319
|
-
JSXText(node) {
|
|
320
|
-
const value = node.value.trim();
|
|
321
|
-
if (!value) return;
|
|
322
|
-
if (isIgnoredWord(value)) return;
|
|
323
|
-
if (shouldIgnoreParent(node)) return;
|
|
324
|
-
if (!hasFbtParent(node)) {
|
|
325
|
-
const startOffset = node.range[0] + node.value.indexOf(value);
|
|
326
|
-
const endOffset = startOffset + value.length;
|
|
327
|
-
context.report({
|
|
328
|
-
loc: {
|
|
329
|
-
end: context.sourceCode.getLocFromIndex(endOffset),
|
|
330
|
-
start: context.sourceCode.getLocFromIndex(startOffset)
|
|
331
|
-
},
|
|
332
|
-
messageId: "unwrappedString"
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
};
|
|
337
|
-
},
|
|
338
|
-
defaultOptions: [{ ignoredWords: [] }],
|
|
339
|
-
meta: {
|
|
340
|
-
docs: { description: "Enforce text content to be wrapped with `<fbt>`, `fbt()` or `fbs()`" },
|
|
341
|
-
messages: { unwrappedString: "Text content must be wrapped in `<fbt>`, `fbt()` or `fbs()`" },
|
|
342
|
-
schema: [{
|
|
343
|
-
additionalProperties: false,
|
|
344
|
-
properties: { ignoredWords: {
|
|
345
|
-
items: { type: "string" },
|
|
346
|
-
type: "array",
|
|
347
|
-
uniqueItems: true
|
|
348
|
-
} },
|
|
349
|
-
type: "object"
|
|
350
|
-
}],
|
|
351
|
-
type: "problem"
|
|
352
|
-
},
|
|
353
|
-
name: "no-untranslated-strings"
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
//#endregion
|
|
357
|
-
//#region src/index.tsx
|
|
358
|
-
var src_default = {
|
|
359
|
-
configs: {
|
|
360
|
-
recommended: { rules: {
|
|
361
|
-
"@nkzw/fbtee/no-empty-strings": 2,
|
|
362
|
-
"@nkzw/fbtee/no-unhelpful-desc": 2
|
|
363
|
-
} },
|
|
364
|
-
strict: { rules: {
|
|
365
|
-
"@nkzw/fbtee/no-empty-strings": 2,
|
|
366
|
-
"@nkzw/fbtee/no-unhelpful-desc": 2,
|
|
367
|
-
"@nkzw/fbtee/no-untranslated-strings": 2
|
|
368
|
-
} }
|
|
369
|
-
},
|
|
370
|
-
meta: {
|
|
371
|
-
name: package_default.name,
|
|
372
|
-
version: package_default.version
|
|
373
|
-
},
|
|
374
|
-
rules: {
|
|
375
|
-
"no-empty-strings": no_empty_strings_default,
|
|
376
|
-
"no-unhelpful-desc": no_unhelpful_desc_default,
|
|
377
|
-
"no-untranslated-strings": no_untranslated_strings_default
|
|
378
|
-
}
|
|
379
|
-
};
|
|
380
|
-
|
|
381
|
-
//#endregion
|
|
382
|
-
module.exports = src_default;
|
package/lib/index.d.cts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import * as _typescript_eslint_utils_ts_eslint0 from "@typescript-eslint/utils/ts-eslint";
|
|
2
|
-
|
|
3
|
-
//#region src/index.d.ts
|
|
4
|
-
declare const _default: {
|
|
5
|
-
configs: {
|
|
6
|
-
recommended: {
|
|
7
|
-
rules: {
|
|
8
|
-
'@nkzw/fbtee/no-empty-strings': number;
|
|
9
|
-
'@nkzw/fbtee/no-unhelpful-desc': number;
|
|
10
|
-
};
|
|
11
|
-
};
|
|
12
|
-
strict: {
|
|
13
|
-
rules: {
|
|
14
|
-
'@nkzw/fbtee/no-empty-strings': number;
|
|
15
|
-
'@nkzw/fbtee/no-unhelpful-desc': number;
|
|
16
|
-
'@nkzw/fbtee/no-untranslated-strings': number;
|
|
17
|
-
};
|
|
18
|
-
};
|
|
19
|
-
};
|
|
20
|
-
meta: {
|
|
21
|
-
name: string;
|
|
22
|
-
version: string;
|
|
23
|
-
};
|
|
24
|
-
rules: {
|
|
25
|
-
'no-empty-strings': _typescript_eslint_utils_ts_eslint0.RuleModule<"emptyString" | "jsxEmptyString", [], unknown, _typescript_eslint_utils_ts_eslint0.RuleListener>;
|
|
26
|
-
'no-unhelpful-desc': _typescript_eslint_utils_ts_eslint0.RuleModule<"emptyDesc" | "jsxEmptyDesc" | "duplicateDesc" | "shortDesc", [], unknown, _typescript_eslint_utils_ts_eslint0.RuleListener>;
|
|
27
|
-
'no-untranslated-strings': _typescript_eslint_utils_ts_eslint0.RuleModule<"unwrappedString", [{
|
|
28
|
-
ignoredWords: Array<string>;
|
|
29
|
-
}], unknown, _typescript_eslint_utils_ts_eslint0.RuleListener>;
|
|
30
|
-
};
|
|
31
|
-
};
|
|
32
|
-
export = _default;
|