@nkzw/eslint-plugin-fbtee 0.1.3 → 0.1.4
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 +13 -3
- package/docs/rules/no-unhelpful-desc.md +1 -1
- package/docs/rules/no-unwrapped-strings.md +55 -0
- package/lib/index.cjs +179 -34
- package/lib/index.d.cts +10 -0
- package/lib/index.d.ts +10 -0
- package/lib/index.js +179 -34
- package/package.json +1 -1
- package/src/__tests__/no-empty-strings-test.tsx +93 -3
- package/src/__tests__/no-unhelpful-desc-test.tsx +20 -0
- package/src/__tests__/no-untranslated-strings-test.tsx +223 -0
- package/src/index.tsx +9 -0
- package/src/rules/no-empty-strings.tsx +37 -41
- package/src/rules/no-unhelpful-desc.tsx +3 -6
- package/src/rules/no-untranslated-strings.tsx +141 -0
- package/src/utils.tsx +50 -2
package/lib/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// package.json
|
|
2
2
|
var package_default = {
|
|
3
3
|
name: "@nkzw/eslint-plugin-fbtee",
|
|
4
|
-
version: "0.1.
|
|
4
|
+
version: "0.1.4",
|
|
5
5
|
description: "",
|
|
6
6
|
keywords: [],
|
|
7
7
|
repository: {
|
|
@@ -29,7 +29,7 @@ var package_default = {
|
|
|
29
29
|
// src/utils.tsx
|
|
30
30
|
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
31
31
|
var createRule = ESLintUtils.RuleCreator(
|
|
32
|
-
(name) => `https://github.com/nkzw-tech/fbtee/
|
|
32
|
+
(name) => `https://github.com/nkzw-tech/fbtee/blob/main/packages/eslint-plugin-fbtee/docs/rules/${name}.md`
|
|
33
33
|
);
|
|
34
34
|
function resolveMemberExpressions(object, property) {
|
|
35
35
|
if (object.type === "JSXMemberExpression") {
|
|
@@ -95,13 +95,47 @@ function resolveNodeValue(node) {
|
|
|
95
95
|
}
|
|
96
96
|
return null;
|
|
97
97
|
}
|
|
98
|
+
function getPropName(prop) {
|
|
99
|
+
if (prop.name.type === "JSXNamespacedName") {
|
|
100
|
+
return `${prop.name.namespace.name}:${prop.name.name.name}`;
|
|
101
|
+
}
|
|
102
|
+
return prop.name.name;
|
|
103
|
+
}
|
|
104
|
+
function hasFbtParent(node) {
|
|
105
|
+
let current = node.parent;
|
|
106
|
+
while (current) {
|
|
107
|
+
if (isFbtNode(current)) {
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
current = current.parent;
|
|
111
|
+
}
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
function isFbtNode(node) {
|
|
115
|
+
if (!node) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
if (node.type === "JSXElement") {
|
|
119
|
+
const name = elementType(node);
|
|
120
|
+
return name === "fbt" || name === "fbs" || name?.startsWith("fbt:");
|
|
121
|
+
}
|
|
122
|
+
if (node.type === "CallExpression" && node.callee.type === "Identifier") {
|
|
123
|
+
const name = node.callee.name;
|
|
124
|
+
return name === "fbt" || name === "fbs";
|
|
125
|
+
}
|
|
126
|
+
if (node.type === "MemberExpression" && node.object.type === "CallExpression" && node.object.callee.type === "Identifier") {
|
|
127
|
+
const name = node.object.callee.name;
|
|
128
|
+
return name === "fbt" || name === "fbs";
|
|
129
|
+
}
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
98
132
|
|
|
99
133
|
// src/rules/no-empty-strings.tsx
|
|
100
134
|
var no_empty_strings_default = createRule({
|
|
101
135
|
create(context) {
|
|
102
136
|
return {
|
|
103
137
|
CallExpression(node) {
|
|
104
|
-
if (
|
|
138
|
+
if (!isFbtNode(node)) {
|
|
105
139
|
return;
|
|
106
140
|
}
|
|
107
141
|
const [text] = node.arguments;
|
|
@@ -113,18 +147,18 @@ var no_empty_strings_default = createRule({
|
|
|
113
147
|
}
|
|
114
148
|
},
|
|
115
149
|
JSXElement(node) {
|
|
116
|
-
if (
|
|
150
|
+
if (!isFbtNode(node)) {
|
|
117
151
|
return;
|
|
118
152
|
}
|
|
119
|
-
|
|
120
|
-
|
|
153
|
+
if (node.openingElement.name.type === "JSXNamespacedName") {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const hasTextContent = validateChildren(node);
|
|
121
157
|
if (!hasTextContent) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
});
|
|
127
|
-
}
|
|
158
|
+
context.report({
|
|
159
|
+
messageId: "jsxEmptyString",
|
|
160
|
+
node
|
|
161
|
+
});
|
|
128
162
|
}
|
|
129
163
|
}
|
|
130
164
|
};
|
|
@@ -135,30 +169,30 @@ var no_empty_strings_default = createRule({
|
|
|
135
169
|
description: "Disallow empty strings in fbt elements or function calls."
|
|
136
170
|
},
|
|
137
171
|
messages: {
|
|
138
|
-
emptyString: "Empty strings are not allowed in fbt()
|
|
139
|
-
jsxEmptyString: "Empty strings are not allowed as children of
|
|
172
|
+
emptyString: "Empty strings are not allowed in fbt() and fbs() function arguments.",
|
|
173
|
+
jsxEmptyString: "Empty strings are not allowed as children of `<fbt>` and `<fbs>` tags."
|
|
140
174
|
},
|
|
141
175
|
schema: [],
|
|
142
176
|
type: "problem"
|
|
143
177
|
},
|
|
144
178
|
name: "no-empty-strings"
|
|
145
179
|
});
|
|
146
|
-
function validateChildren(node
|
|
180
|
+
function validateChildren(node) {
|
|
147
181
|
let hasTextContent = false;
|
|
148
182
|
for (const child of node.children) {
|
|
149
|
-
if (child.type === "JSXText" &&
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
183
|
+
if (child.type === "JSXText" && child.value.trim() !== "") {
|
|
184
|
+
hasTextContent = true;
|
|
185
|
+
}
|
|
186
|
+
if (child.type === "JSXExpressionContainer") {
|
|
187
|
+
if (child.expression.type === "JSXEmptyExpression") {
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (child.expression.type === "Identifier" && child.expression.name !== "undefined") {
|
|
153
191
|
hasTextContent = true;
|
|
192
|
+
continue;
|
|
154
193
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
if (isEmptyString(child.expression) && (node.children.length === 1 || node.children.every(
|
|
158
|
-
(otherChild) => child === otherChild || otherChild.type === "JSXText" && otherChild.value.trim() === ""
|
|
159
|
-
))) {
|
|
160
|
-
nodesToReport.add(child.expression);
|
|
161
|
-
} else {
|
|
194
|
+
const value = resolveNodeValue(child.expression)?.trim();
|
|
195
|
+
if (value) {
|
|
162
196
|
hasTextContent = true;
|
|
163
197
|
}
|
|
164
198
|
}
|
|
@@ -168,15 +202,12 @@ function validateChildren(node, nodesToReport) {
|
|
|
168
202
|
hasTextContent = true;
|
|
169
203
|
continue;
|
|
170
204
|
}
|
|
171
|
-
hasTextContent ||= validateChildren(child
|
|
205
|
+
hasTextContent ||= validateChildren(child);
|
|
172
206
|
}
|
|
173
207
|
if (hasTextContent) {
|
|
174
208
|
break;
|
|
175
209
|
}
|
|
176
210
|
}
|
|
177
|
-
if (!hasTextContent && nodesToReport.size === 0) {
|
|
178
|
-
nodesToReport.add(node);
|
|
179
|
-
}
|
|
180
211
|
return hasTextContent;
|
|
181
212
|
}
|
|
182
213
|
function isEmptyString(node) {
|
|
@@ -188,7 +219,7 @@ var no_unhelpful_desc_default = createRule({
|
|
|
188
219
|
create(context) {
|
|
189
220
|
return {
|
|
190
221
|
CallExpression(node) {
|
|
191
|
-
if (
|
|
222
|
+
if (!isFbtNode(node)) {
|
|
192
223
|
return;
|
|
193
224
|
}
|
|
194
225
|
const [textArg, descArg] = node.arguments;
|
|
@@ -216,7 +247,7 @@ var no_unhelpful_desc_default = createRule({
|
|
|
216
247
|
}
|
|
217
248
|
},
|
|
218
249
|
JSXAttribute(node) {
|
|
219
|
-
if (
|
|
250
|
+
if (!isFbtNode(node.parent.parent)) {
|
|
220
251
|
return;
|
|
221
252
|
}
|
|
222
253
|
if (node.name.name !== "desc" || !node.value) {
|
|
@@ -267,6 +298,112 @@ var no_unhelpful_desc_default = createRule({
|
|
|
267
298
|
name: "no-unhelpful-desc"
|
|
268
299
|
});
|
|
269
300
|
|
|
301
|
+
// src/rules/no-untranslated-strings.tsx
|
|
302
|
+
var attributes = /* @__PURE__ */ new Set([
|
|
303
|
+
"title",
|
|
304
|
+
"placeholder",
|
|
305
|
+
"alt",
|
|
306
|
+
"label",
|
|
307
|
+
"aria-label",
|
|
308
|
+
"aria-errormessage"
|
|
309
|
+
]);
|
|
310
|
+
var no_untranslated_strings_default = createRule({
|
|
311
|
+
create(context, options) {
|
|
312
|
+
const ignoredWords = new Set(
|
|
313
|
+
options[0].ignoredWords.map((s) => s.trim().toLowerCase())
|
|
314
|
+
);
|
|
315
|
+
function isIgnoredWord(value) {
|
|
316
|
+
return ignoredWords.has(value.trim().toLowerCase());
|
|
317
|
+
}
|
|
318
|
+
return {
|
|
319
|
+
JSXAttribute(node) {
|
|
320
|
+
const propName = getPropName(node);
|
|
321
|
+
if (!attributes.has(propName)) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
const value = node.value ? resolveNodeValue(node.value) : null;
|
|
325
|
+
if (!value) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
if (isIgnoredWord(value)) {
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
if (node.value?.type === "JSXExpressionContainer" && isFbtNode(node.value.expression)) {
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
context.report({
|
|
335
|
+
messageId: "unwrappedString",
|
|
336
|
+
node
|
|
337
|
+
});
|
|
338
|
+
},
|
|
339
|
+
JSXExpressionContainer(node) {
|
|
340
|
+
if (node.parent.type === "JSXAttribute") {
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
const value = resolveNodeValue(node.expression);
|
|
344
|
+
if (!value) {
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
if (isIgnoredWord(value)) {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
if (!hasFbtParent(node)) {
|
|
351
|
+
context.report({
|
|
352
|
+
messageId: "unwrappedString",
|
|
353
|
+
node
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
JSXText(node) {
|
|
358
|
+
const value = node.value.trim();
|
|
359
|
+
if (!value) {
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
if (isIgnoredWord(value)) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
if (!hasFbtParent(node)) {
|
|
366
|
+
const startOffset = node.range[0] + node.value.indexOf(value);
|
|
367
|
+
const endOffset = startOffset + value.length;
|
|
368
|
+
context.report({
|
|
369
|
+
loc: {
|
|
370
|
+
end: context.sourceCode.getLocFromIndex(endOffset),
|
|
371
|
+
start: context.sourceCode.getLocFromIndex(startOffset)
|
|
372
|
+
},
|
|
373
|
+
messageId: "unwrappedString"
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
},
|
|
379
|
+
defaultOptions: [{ ignoredWords: [] }],
|
|
380
|
+
meta: {
|
|
381
|
+
docs: {
|
|
382
|
+
description: "Enforce text content to be wrapped with `<fbt>`, `fbt()` or `fbs()`"
|
|
383
|
+
},
|
|
384
|
+
messages: {
|
|
385
|
+
unwrappedString: "Text content must be wrapped in `<fbt>`, `fbt()` or `fbs()`"
|
|
386
|
+
},
|
|
387
|
+
schema: [
|
|
388
|
+
{
|
|
389
|
+
additionalProperties: false,
|
|
390
|
+
properties: {
|
|
391
|
+
ignoredWords: {
|
|
392
|
+
items: {
|
|
393
|
+
type: "string"
|
|
394
|
+
},
|
|
395
|
+
type: "array",
|
|
396
|
+
uniqueItems: true
|
|
397
|
+
}
|
|
398
|
+
},
|
|
399
|
+
type: "object"
|
|
400
|
+
}
|
|
401
|
+
],
|
|
402
|
+
type: "problem"
|
|
403
|
+
},
|
|
404
|
+
name: "no-untranslated-strings"
|
|
405
|
+
});
|
|
406
|
+
|
|
270
407
|
// src/index.tsx
|
|
271
408
|
var meta = {
|
|
272
409
|
name: package_default.name,
|
|
@@ -274,14 +411,22 @@ var meta = {
|
|
|
274
411
|
};
|
|
275
412
|
var rules = {
|
|
276
413
|
"no-empty-strings": no_empty_strings_default,
|
|
277
|
-
"no-unhelpful-desc": no_unhelpful_desc_default
|
|
414
|
+
"no-unhelpful-desc": no_unhelpful_desc_default,
|
|
415
|
+
"no-untranslated-strings": no_untranslated_strings_default
|
|
278
416
|
};
|
|
279
417
|
var configs = {
|
|
280
|
-
|
|
418
|
+
recommended: {
|
|
281
419
|
rules: {
|
|
282
420
|
"@nkzw/fbtee/no-empty-strings": 2,
|
|
283
421
|
"@nkzw/fbtee/no-unhelpful-desc": 2
|
|
284
422
|
}
|
|
423
|
+
},
|
|
424
|
+
strict: {
|
|
425
|
+
rules: {
|
|
426
|
+
"@nkzw/fbtee/no-empty-strings": 2,
|
|
427
|
+
"@nkzw/fbtee/no-unhelpful-desc": 2,
|
|
428
|
+
"@nkzw/fbtee/no-untranslated-strings": 2
|
|
429
|
+
}
|
|
285
430
|
}
|
|
286
431
|
};
|
|
287
432
|
export {
|
package/package.json
CHANGED
|
@@ -66,7 +66,70 @@ ruleTester.run('no-empty-strings', rule, {
|
|
|
66
66
|
`,
|
|
67
67
|
errors: [
|
|
68
68
|
{
|
|
69
|
-
line:
|
|
69
|
+
line: 2,
|
|
70
|
+
messageId: 'jsxEmptyString',
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
code: `
|
|
76
|
+
<fbt desc="Greeting">{}</fbt>;
|
|
77
|
+
`,
|
|
78
|
+
errors: [
|
|
79
|
+
{
|
|
80
|
+
line: 2,
|
|
81
|
+
messageId: 'jsxEmptyString',
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
code: `
|
|
87
|
+
<fbt desc="Greeting">
|
|
88
|
+
{}
|
|
89
|
+
</fbt>;
|
|
90
|
+
`,
|
|
91
|
+
errors: [
|
|
92
|
+
{
|
|
93
|
+
line: 2,
|
|
94
|
+
messageId: 'jsxEmptyString',
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
code: `
|
|
100
|
+
<fbt desc="Greeting">
|
|
101
|
+
{null}
|
|
102
|
+
</fbt>;
|
|
103
|
+
`,
|
|
104
|
+
errors: [
|
|
105
|
+
{
|
|
106
|
+
line: 2,
|
|
107
|
+
messageId: 'jsxEmptyString',
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
code: `
|
|
113
|
+
<fbt desc="Greeting">
|
|
114
|
+
{undefined}
|
|
115
|
+
</fbt>;
|
|
116
|
+
`,
|
|
117
|
+
errors: [
|
|
118
|
+
{
|
|
119
|
+
line: 2,
|
|
120
|
+
messageId: 'jsxEmptyString',
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
code: `
|
|
126
|
+
<fbt desc="Greeting">
|
|
127
|
+
{/* This is a comment */}
|
|
128
|
+
</fbt>;
|
|
129
|
+
`,
|
|
130
|
+
errors: [
|
|
131
|
+
{
|
|
132
|
+
line: 2,
|
|
70
133
|
messageId: 'jsxEmptyString',
|
|
71
134
|
},
|
|
72
135
|
],
|
|
@@ -79,7 +142,7 @@ ruleTester.run('no-empty-strings', rule, {
|
|
|
79
142
|
`,
|
|
80
143
|
errors: [
|
|
81
144
|
{
|
|
82
|
-
line:
|
|
145
|
+
line: 2,
|
|
83
146
|
messageId: 'jsxEmptyString',
|
|
84
147
|
},
|
|
85
148
|
],
|
|
@@ -92,7 +155,20 @@ ruleTester.run('no-empty-strings', rule, {
|
|
|
92
155
|
`,
|
|
93
156
|
errors: [
|
|
94
157
|
{
|
|
95
|
-
line:
|
|
158
|
+
line: 2,
|
|
159
|
+
messageId: 'jsxEmptyString',
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
// <fbs>
|
|
165
|
+
{
|
|
166
|
+
code: `
|
|
167
|
+
<fbs desc="Greeting"></fbs>;
|
|
168
|
+
`,
|
|
169
|
+
errors: [
|
|
170
|
+
{
|
|
171
|
+
line: 2,
|
|
96
172
|
messageId: 'jsxEmptyString',
|
|
97
173
|
},
|
|
98
174
|
],
|
|
@@ -164,6 +240,13 @@ ruleTester.run('no-empty-strings', rule, {
|
|
|
164
240
|
<fbt desc="Greeting">Hello world</fbt>;
|
|
165
241
|
`,
|
|
166
242
|
},
|
|
243
|
+
{
|
|
244
|
+
code: `
|
|
245
|
+
<fbt desc="Greeting">
|
|
246
|
+
Hello <span>world</span>
|
|
247
|
+
</fbt>;
|
|
248
|
+
`,
|
|
249
|
+
},
|
|
167
250
|
{
|
|
168
251
|
code: `
|
|
169
252
|
<fbt desc="Greeting">{dynamicValue}</fbt>;
|
|
@@ -240,6 +323,13 @@ ruleTester.run('no-empty-strings', rule, {
|
|
|
240
323
|
`,
|
|
241
324
|
},
|
|
242
325
|
|
|
326
|
+
// <fbs>
|
|
327
|
+
{
|
|
328
|
+
code: `
|
|
329
|
+
<fbs desc="Greeting">Hello world</fbs>;
|
|
330
|
+
`,
|
|
331
|
+
},
|
|
332
|
+
|
|
243
333
|
// fbt()
|
|
244
334
|
{
|
|
245
335
|
code: `
|
|
@@ -114,6 +114,19 @@ ruleTester.run('unhelpful-desc', rule, {
|
|
|
114
114
|
],
|
|
115
115
|
},
|
|
116
116
|
|
|
117
|
+
// <fbs>
|
|
118
|
+
{
|
|
119
|
+
code: `
|
|
120
|
+
<fbt desc="">Hello world</fbt>;
|
|
121
|
+
`,
|
|
122
|
+
errors: [
|
|
123
|
+
{
|
|
124
|
+
line: 2,
|
|
125
|
+
messageId: 'jsxEmptyDesc',
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
},
|
|
129
|
+
|
|
117
130
|
// fbt()
|
|
118
131
|
{
|
|
119
132
|
code: `
|
|
@@ -274,6 +287,13 @@ ruleTester.run('unhelpful-desc', rule, {
|
|
|
274
287
|
`,
|
|
275
288
|
},
|
|
276
289
|
|
|
290
|
+
// <fbs>
|
|
291
|
+
{
|
|
292
|
+
code: `
|
|
293
|
+
<fbs desc="Greeting">Hello world</fbs>;
|
|
294
|
+
`,
|
|
295
|
+
},
|
|
296
|
+
|
|
277
297
|
// fbt()
|
|
278
298
|
{
|
|
279
299
|
code: `
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { RuleTester } from '@typescript-eslint/rule-tester';
|
|
2
|
+
import rule from '../rules/no-untranslated-strings.tsx';
|
|
3
|
+
|
|
4
|
+
const ruleTester = new RuleTester({
|
|
5
|
+
languageOptions: {
|
|
6
|
+
parserOptions: {
|
|
7
|
+
ecmaFeatures: {
|
|
8
|
+
jsx: true,
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
ruleTester.run('no-untranslated-strings', rule, {
|
|
15
|
+
invalid: [
|
|
16
|
+
{
|
|
17
|
+
code: `
|
|
18
|
+
<span>Hello world</span>;
|
|
19
|
+
`,
|
|
20
|
+
errors: [
|
|
21
|
+
{
|
|
22
|
+
messageId: 'unwrappedString',
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
code: `
|
|
28
|
+
<span>{'Hello world'}</span>;
|
|
29
|
+
`,
|
|
30
|
+
errors: [
|
|
31
|
+
{
|
|
32
|
+
messageId: 'unwrappedString',
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
code: `
|
|
38
|
+
<span>{'Hello world' + ''}</span>;
|
|
39
|
+
`,
|
|
40
|
+
errors: [
|
|
41
|
+
{
|
|
42
|
+
messageId: 'unwrappedString',
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
code: `
|
|
48
|
+
<div>
|
|
49
|
+
<span>Hello world</span>
|
|
50
|
+
</div>;
|
|
51
|
+
`,
|
|
52
|
+
errors: [
|
|
53
|
+
{
|
|
54
|
+
line: 3,
|
|
55
|
+
messageId: 'unwrappedString',
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
code: `
|
|
61
|
+
<div>
|
|
62
|
+
<div></div>
|
|
63
|
+
Hello world
|
|
64
|
+
</div>;
|
|
65
|
+
`,
|
|
66
|
+
errors: [
|
|
67
|
+
{
|
|
68
|
+
line: 4,
|
|
69
|
+
messageId: 'unwrappedString',
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
code: `
|
|
75
|
+
<>Hello world</>
|
|
76
|
+
`,
|
|
77
|
+
errors: [
|
|
78
|
+
{
|
|
79
|
+
messageId: 'unwrappedString',
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
code: `
|
|
85
|
+
<input placeholder="Enter name..." />;
|
|
86
|
+
`,
|
|
87
|
+
errors: [
|
|
88
|
+
{
|
|
89
|
+
messageId: 'unwrappedString',
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
code: `
|
|
95
|
+
<div aria-label={'Label'}></div>
|
|
96
|
+
`,
|
|
97
|
+
errors: [
|
|
98
|
+
{
|
|
99
|
+
messageId: 'unwrappedString',
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
code: `
|
|
105
|
+
<div aria-label={\`Label\`}></div>
|
|
106
|
+
`,
|
|
107
|
+
errors: [
|
|
108
|
+
{
|
|
109
|
+
messageId: 'unwrappedString',
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
|
|
115
|
+
valid: [
|
|
116
|
+
{
|
|
117
|
+
code: `
|
|
118
|
+
<fbt desc="Greeting">Hello world</fbt>
|
|
119
|
+
`,
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
code: `
|
|
123
|
+
<fbt desc="Greeting">
|
|
124
|
+
Hello world
|
|
125
|
+
</fbt>
|
|
126
|
+
`,
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
code: `
|
|
130
|
+
<fbt desc="Greeting">
|
|
131
|
+
<span>Hello world</span>
|
|
132
|
+
</fbt>
|
|
133
|
+
`,
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
code: `
|
|
137
|
+
<span>
|
|
138
|
+
{fbt('Hello', 'Greeting')}
|
|
139
|
+
</span>
|
|
140
|
+
`,
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
code: `
|
|
144
|
+
<fbt desc="Counter">
|
|
145
|
+
Count:
|
|
146
|
+
<fbt:plural count={1} showCount="yes" many="items">
|
|
147
|
+
item
|
|
148
|
+
</fbt:plural>
|
|
149
|
+
</fbt>
|
|
150
|
+
`,
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
code: `
|
|
154
|
+
<fbt desc="Greeting">
|
|
155
|
+
<>Hello</>
|
|
156
|
+
</fbt>
|
|
157
|
+
`,
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
code: `
|
|
161
|
+
<fbt desc="Greeting">
|
|
162
|
+
Hello <span>world</span>
|
|
163
|
+
</fbt>;
|
|
164
|
+
`,
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
code: `
|
|
168
|
+
<fbt desc="Welcome message">
|
|
169
|
+
Welcome <fbt:param name="name">Steve</fbt:param>
|
|
170
|
+
</fbt>
|
|
171
|
+
`,
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
code: `
|
|
175
|
+
<div>
|
|
176
|
+
<p>GitHub</p>
|
|
177
|
+
<p>{'GitHub'}</p>
|
|
178
|
+
<p>{\`GitHub\`}</p>
|
|
179
|
+
</div>
|
|
180
|
+
`,
|
|
181
|
+
options: [
|
|
182
|
+
{
|
|
183
|
+
ignoredWords: ['GitHub'],
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
code: `
|
|
189
|
+
<textarea rows={2}></textarea>
|
|
190
|
+
`,
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
code: `
|
|
194
|
+
<a aria-current></a>
|
|
195
|
+
`,
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
code: `
|
|
199
|
+
<img src="" alt={fbs('A photo of a dog', 'Image description')} />
|
|
200
|
+
`,
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
code: `
|
|
204
|
+
<div aria-label={fbs('Hello', 'Greeting')}></div>
|
|
205
|
+
`,
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
code: `
|
|
209
|
+
<div aria-label={fbt('Hello', 'Greeting').toString()}></div>
|
|
210
|
+
`,
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
code: `
|
|
214
|
+
foo('Bar')
|
|
215
|
+
`,
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
code: `
|
|
219
|
+
foo.bar('Baz')
|
|
220
|
+
`,
|
|
221
|
+
},
|
|
222
|
+
],
|
|
223
|
+
});
|