eslint-plugin-formatjs 6.4.3 → 6.4.5
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/index.d.ts +15 -12
- package/index.js +4603 -54
- package/index.js.map +1 -0
- package/package.json +3 -3
- package/util.d.ts +654 -29
- package/util.js +62 -134
- package/util.js.map +1 -0
- package/emoji-data.generated.d.ts +0 -27
- package/emoji-data.generated.js +0 -2564
- package/emoji-utils.d.ts +0 -43
- package/emoji-utils.js +0 -145
- package/messages.d.ts +0 -2
- package/messages.js +0 -1
- package/rules/blocklist-elements.d.ts +0 -14
- package/rules/blocklist-elements.js +0 -129
- package/rules/enforce-default-message.d.ts +0 -7
- package/rules/enforce-default-message.js +0 -57
- package/rules/enforce-description.d.ts +0 -11
- package/rules/enforce-description.js +0 -97
- package/rules/enforce-id.d.ts +0 -8
- package/rules/enforce-id.js +0 -135
- package/rules/enforce-placeholders.d.ts +0 -3
- package/rules/enforce-placeholders.js +0 -128
- package/rules/enforce-plural-rules.d.ts +0 -14
- package/rules/enforce-plural-rules.js +0 -108
- package/rules/no-camel-case.d.ts +0 -3
- package/rules/no-camel-case.js +0 -85
- package/rules/no-complex-selectors.d.ts +0 -3
- package/rules/no-complex-selectors.js +0 -119
- package/rules/no-emoji.d.ts +0 -8
- package/rules/no-emoji.js +0 -88
- package/rules/no-id.d.ts +0 -3
- package/rules/no-id.js +0 -48
- package/rules/no-invalid-icu.d.ts +0 -3
- package/rules/no-invalid-icu.js +0 -56
- package/rules/no-literal-string-in-jsx.d.ts +0 -3
- package/rules/no-literal-string-in-jsx.js +0 -161
- package/rules/no-literal-string-in-object.d.ts +0 -3
- package/rules/no-literal-string-in-object.js +0 -59
- package/rules/no-missing-icu-plural-one-placeholders.d.ts +0 -5
- package/rules/no-missing-icu-plural-one-placeholders.js +0 -94
- package/rules/no-multiple-plurals.d.ts +0 -3
- package/rules/no-multiple-plurals.js +0 -76
- package/rules/no-multiple-whitespaces.d.ts +0 -3
- package/rules/no-multiple-whitespaces.js +0 -126
- package/rules/no-offset.d.ts +0 -3
- package/rules/no-offset.js +0 -75
- package/rules/no-useless-message.d.ts +0 -3
- package/rules/no-useless-message.js +0 -69
- package/rules/prefer-formatted-message.d.ts +0 -3
- package/rules/prefer-formatted-message.js +0 -26
- package/rules/prefer-full-sentence.d.ts +0 -3
- package/rules/prefer-full-sentence.js +0 -111
- package/rules/prefer-pound-in-plural.d.ts +0 -3
- package/rules/prefer-pound-in-plural.js +0 -163
package/index.js
CHANGED
|
@@ -1,62 +1,4609 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
[
|
|
40
|
-
[
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
[
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
1
|
+
import { TYPE, isArgumentElement, isDateElement, isLiteralElement, isNumberElement, isPluralElement, isSelectElement, isTagElement, isTimeElement, parse } from "@formatjs/icu-messageformat-parser";
|
|
2
|
+
import { interpolateName } from "@formatjs/ts-transformer";
|
|
3
|
+
import * as emojiPresentationRegex from "@unicode/unicode-17.0.0/Binary_Property/Emoji_Presentation/regex.js";
|
|
4
|
+
import * as emojiPropertyRegex from "@unicode/unicode-17.0.0/Binary_Property/Emoji/regex.js";
|
|
5
|
+
import * as picomatchNs from "picomatch";
|
|
6
|
+
import MagicString from "magic-string";
|
|
7
|
+
//#region \0rolldown/runtime.js
|
|
8
|
+
var __defProp = Object.defineProperty;
|
|
9
|
+
var __exportAll = (all, no_symbols) => {
|
|
10
|
+
let target = {};
|
|
11
|
+
for (var name in all) __defProp(target, name, {
|
|
12
|
+
get: all[name],
|
|
13
|
+
enumerable: true
|
|
14
|
+
});
|
|
15
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
16
|
+
return target;
|
|
17
|
+
};
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region packages/eslint-plugin-formatjs/util.ts
|
|
20
|
+
const FORMAT_FUNCTION_NAMES = new Set([
|
|
21
|
+
"$formatMessage",
|
|
22
|
+
"formatMessage",
|
|
23
|
+
"$t"
|
|
24
|
+
]);
|
|
25
|
+
const COMPONENT_NAMES = new Set(["FormattedMessage"]);
|
|
26
|
+
const DECLARATION_FUNCTION_NAMES = new Set(["defineMessage"]);
|
|
27
|
+
function getSettings({ settings }) {
|
|
28
|
+
return settings.formatjs ?? settings;
|
|
29
|
+
}
|
|
30
|
+
function isStringLiteral(node) {
|
|
31
|
+
return node.type === "Literal" && typeof node.value === "string";
|
|
32
|
+
}
|
|
33
|
+
function isTemplateLiteralWithoutVar(node) {
|
|
34
|
+
return node.type === "TemplateLiteral" && node.quasis.length === 1;
|
|
35
|
+
}
|
|
36
|
+
function staticallyEvaluateStringConcat(node) {
|
|
37
|
+
const right = node.right;
|
|
38
|
+
const left = node.left;
|
|
39
|
+
if (!isStringLiteral(right)) return ["", false];
|
|
40
|
+
if (isStringLiteral(left)) return [left.value + right.value, true];
|
|
41
|
+
if (node.left.type === "BinaryExpression") {
|
|
42
|
+
const [result, isStaticallyEvaluatable] = staticallyEvaluateStringConcat(node.left);
|
|
43
|
+
return [result + right.value, isStaticallyEvaluatable];
|
|
44
|
+
}
|
|
45
|
+
return ["", false];
|
|
46
|
+
}
|
|
47
|
+
function isIntlFormatMessageCall(node) {
|
|
48
|
+
if (node.type !== "CallExpression") return false;
|
|
49
|
+
if (node.arguments.length < 1 || node.arguments[0].type !== "ObjectExpression") return false;
|
|
50
|
+
if (node.callee.type === "MemberExpression") return (node.callee.object.type === "Identifier" && node.callee.object.name === "intl" || node.callee.object.type === "MemberExpression" && node.callee.object.property.type === "Identifier" && node.callee.object.property.name === "intl") && node.callee.property.type === "Identifier" && (node.callee.property.name === "formatMessage" || node.callee.property.name === "$t");
|
|
51
|
+
if (node.callee.type === "Identifier") return FORMAT_FUNCTION_NAMES.has(node.callee.name);
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
function isSingleMessageDescriptorDeclaration(node, functionNames) {
|
|
55
|
+
return node.type === "CallExpression" && node.callee.type === "Identifier" && functionNames.has(node.callee.name);
|
|
56
|
+
}
|
|
57
|
+
function isMultipleMessageDescriptorDeclaration(node) {
|
|
58
|
+
return node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "defineMessages";
|
|
59
|
+
}
|
|
60
|
+
function extractMessageDescriptor(node) {
|
|
61
|
+
if (!node || node.type !== "ObjectExpression") return;
|
|
62
|
+
const result = {
|
|
63
|
+
messageDescriptorNode: node,
|
|
64
|
+
message: {},
|
|
65
|
+
messageNode: void 0,
|
|
66
|
+
messagePropNode: void 0,
|
|
67
|
+
descriptionNode: void 0,
|
|
68
|
+
idValueNode: void 0
|
|
69
|
+
};
|
|
70
|
+
for (const prop of node.properties) {
|
|
71
|
+
if (prop.type !== "Property" || prop.key.type !== "Identifier") continue;
|
|
72
|
+
const propName = prop.key.name;
|
|
73
|
+
if (propName !== "id" && propName !== "defaultMessage" && propName !== "description") continue;
|
|
74
|
+
const valueNode = prop.value;
|
|
75
|
+
let value = void 0;
|
|
76
|
+
if (isStringLiteral(valueNode)) value = valueNode.value;
|
|
77
|
+
else if (isTemplateLiteralWithoutVar(valueNode)) value = valueNode.quasis[0].value.cooked ?? void 0;
|
|
78
|
+
else if (valueNode.type === "TaggedTemplateExpression") {
|
|
79
|
+
const { quasi } = valueNode;
|
|
80
|
+
if (!isTemplateLiteralWithoutVar(quasi)) throw new Error("Tagged template expression must be no substitution");
|
|
81
|
+
value = quasi.quasis[0].value.cooked ?? void 0;
|
|
82
|
+
} else if (valueNode.type === "BinaryExpression") {
|
|
83
|
+
const [result, isStatic] = staticallyEvaluateStringConcat(valueNode);
|
|
84
|
+
if (isStatic) value = result;
|
|
85
|
+
}
|
|
86
|
+
switch (propName) {
|
|
87
|
+
case "defaultMessage":
|
|
88
|
+
result.messagePropNode = prop;
|
|
89
|
+
result.messageNode = valueNode;
|
|
90
|
+
result.message.defaultMessage = value;
|
|
91
|
+
break;
|
|
92
|
+
case "description":
|
|
93
|
+
result.descriptionNode = valueNode;
|
|
94
|
+
result.message.description = value;
|
|
95
|
+
break;
|
|
96
|
+
case "id":
|
|
97
|
+
result.message.id = value;
|
|
98
|
+
result.idValueNode = valueNode;
|
|
99
|
+
result.idPropNode = prop;
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
function extractMessageDescriptorFromJSXElement(node) {
|
|
106
|
+
if (!node || !node.attributes) return;
|
|
107
|
+
let values;
|
|
108
|
+
const result = {
|
|
109
|
+
messageDescriptorNode: node,
|
|
110
|
+
message: {},
|
|
111
|
+
messageNode: void 0,
|
|
112
|
+
messagePropNode: void 0,
|
|
113
|
+
descriptionNode: void 0,
|
|
114
|
+
idValueNode: void 0,
|
|
115
|
+
idPropNode: void 0
|
|
116
|
+
};
|
|
117
|
+
let hasSpreadAttribute = false;
|
|
118
|
+
for (const prop of node.attributes) {
|
|
119
|
+
if (prop.type === "JSXSpreadAttribute") hasSpreadAttribute = true;
|
|
120
|
+
if (prop.type !== "JSXAttribute" || prop.name.type !== "JSXIdentifier") continue;
|
|
121
|
+
const keyName = prop.name.name;
|
|
122
|
+
const isMessageProp = keyName === "id" || keyName === "defaultMessage" || keyName === "description";
|
|
123
|
+
let valueNode = prop.value;
|
|
124
|
+
let value = void 0;
|
|
125
|
+
if (valueNode && isMessageProp) {
|
|
126
|
+
if (isStringLiteral(valueNode)) value = valueNode.value;
|
|
127
|
+
else if (valueNode?.type === "JSXExpressionContainer") {
|
|
128
|
+
const { expression } = valueNode;
|
|
129
|
+
if (expression.type === "BinaryExpression") {
|
|
130
|
+
const [result, isStatic] = staticallyEvaluateStringConcat(expression);
|
|
131
|
+
if (isStatic) value = result;
|
|
132
|
+
} else if (isTemplateLiteralWithoutVar(expression)) value = expression.quasis[0].value.cooked ?? void 0;
|
|
133
|
+
else if (expression.type === "TaggedTemplateExpression") {
|
|
134
|
+
const { quasi } = expression;
|
|
135
|
+
if (!isTemplateLiteralWithoutVar(quasi)) throw new Error("Tagged template expression must be no substitution");
|
|
136
|
+
value = quasi.quasis[0].value.cooked ?? void 0;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
switch (keyName) {
|
|
141
|
+
case "defaultMessage":
|
|
142
|
+
result.messagePropNode = prop;
|
|
143
|
+
result.messageNode = valueNode;
|
|
144
|
+
if (value) result.message.defaultMessage = value;
|
|
145
|
+
break;
|
|
146
|
+
case "description":
|
|
147
|
+
result.descriptionNode = valueNode;
|
|
148
|
+
if (value) result.message.description = value;
|
|
149
|
+
break;
|
|
150
|
+
case "id":
|
|
151
|
+
result.idValueNode = valueNode;
|
|
152
|
+
result.idPropNode = prop;
|
|
153
|
+
if (value) result.message.id = value;
|
|
154
|
+
break;
|
|
155
|
+
case "values":
|
|
156
|
+
if (valueNode?.type === "JSXExpressionContainer" && valueNode.expression.type === "ObjectExpression") values = valueNode.expression;
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (!result.messagePropNode && !result.descriptionNode && !result.idPropNode && hasSpreadAttribute) return;
|
|
161
|
+
return [result, values];
|
|
162
|
+
}
|
|
163
|
+
function extractMessageDescriptors(node) {
|
|
164
|
+
if (!node || node.type !== "ObjectExpression" || !node.properties.length) return [];
|
|
165
|
+
const msgs = [];
|
|
166
|
+
for (const prop of node.properties) {
|
|
167
|
+
if (prop.type !== "Property") continue;
|
|
168
|
+
const msg = prop.value;
|
|
169
|
+
if (msg.type !== "ObjectExpression") continue;
|
|
170
|
+
const nodeInfo = extractMessageDescriptor(msg);
|
|
171
|
+
if (nodeInfo) msgs.push(nodeInfo);
|
|
172
|
+
}
|
|
173
|
+
return msgs;
|
|
174
|
+
}
|
|
175
|
+
function extractMessages(node, { additionalComponentNames, additionalFunctionNames, excludeMessageDeclCalls } = {}) {
|
|
176
|
+
const allFormatFunctionNames = Array.isArray(additionalFunctionNames) ? new Set([...Array.from(FORMAT_FUNCTION_NAMES), ...additionalFunctionNames]) : FORMAT_FUNCTION_NAMES;
|
|
177
|
+
const allComponentNames = Array.isArray(additionalComponentNames) ? new Set([...Array.from(COMPONENT_NAMES), ...additionalComponentNames]) : COMPONENT_NAMES;
|
|
178
|
+
if (node.type === "CallExpression") {
|
|
179
|
+
const args0 = node.arguments[0];
|
|
180
|
+
const args1 = node.arguments[1];
|
|
181
|
+
if (!args0 || args0.type === "SpreadElement") return [];
|
|
182
|
+
if (!excludeMessageDeclCalls && isSingleMessageDescriptorDeclaration(node, DECLARATION_FUNCTION_NAMES) || isIntlFormatMessageCall(node) || isSingleMessageDescriptorDeclaration(node, allFormatFunctionNames)) {
|
|
183
|
+
const msgDescriptorNodeInfo = extractMessageDescriptor(args0);
|
|
184
|
+
if (msgDescriptorNodeInfo && (!args1 || args1.type !== "SpreadElement")) return [[msgDescriptorNodeInfo, args1]];
|
|
185
|
+
} else if (!excludeMessageDeclCalls && isMultipleMessageDescriptorDeclaration(node)) return extractMessageDescriptors(args0).map((msg) => [msg, void 0]);
|
|
186
|
+
} else if (node.type === "JSXOpeningElement" && node.name && node.name.type === "JSXIdentifier" && allComponentNames.has(node.name.name)) {
|
|
187
|
+
const msgDescriptorNodeInfo = extractMessageDescriptorFromJSXElement(node);
|
|
188
|
+
if (msgDescriptorNodeInfo) return [msgDescriptorNodeInfo];
|
|
189
|
+
}
|
|
190
|
+
return [];
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Apply changes to the ICU message in code. The return value can be used in
|
|
194
|
+
* `fixer.replaceText(messageNode, <return value>)`. If the return value is null,
|
|
195
|
+
* it means that the patch cannot be applied.
|
|
196
|
+
*/
|
|
197
|
+
function patchMessage(messageNode, ast, patcher) {
|
|
198
|
+
if (messageNode.type === "Literal" && messageNode.value && typeof messageNode.value === "string") return "\"" + patcher(messageNode.value, ast).replace("\"", "\\\"") + "\"";
|
|
199
|
+
else if (messageNode.type === "TemplateLiteral" && messageNode.quasis.length === 1 && messageNode.expressions.length === 0) return "`" + patcher(messageNode.quasis[0].value.cooked, ast).replace(/\\/g, "\\\\").replace(/`/g, "\\`") + "`";
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
//#endregion
|
|
203
|
+
//#region packages/eslint-plugin-formatjs/messages.ts
|
|
204
|
+
const CORE_MESSAGES = { parseError: `Failed to parse message string {{error}}` };
|
|
205
|
+
//#endregion
|
|
206
|
+
//#region packages/eslint-plugin-formatjs/rules/blocklist-elements.ts
|
|
207
|
+
const name$22 = "blocklist-elements";
|
|
208
|
+
function getMessage(type) {
|
|
209
|
+
return {
|
|
210
|
+
messageId: "blocklist",
|
|
211
|
+
data: { type }
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
let Element = /* @__PURE__ */ function(Element) {
|
|
215
|
+
Element["literal"] = "literal";
|
|
216
|
+
Element["argument"] = "argument";
|
|
217
|
+
Element["number"] = "number";
|
|
218
|
+
Element["date"] = "date";
|
|
219
|
+
Element["time"] = "time";
|
|
220
|
+
Element["select"] = "select";
|
|
221
|
+
Element["selectordinal"] = "selectordinal";
|
|
222
|
+
Element["plural"] = "plural";
|
|
223
|
+
Element["tag"] = "tag";
|
|
224
|
+
return Element;
|
|
225
|
+
}({});
|
|
226
|
+
function verifyAst$7(blocklist, ast) {
|
|
227
|
+
const errors = [];
|
|
228
|
+
for (const el of ast) {
|
|
229
|
+
if (isLiteralElement(el) && blocklist.includes(Element.literal)) errors.push(getMessage(Element.literal));
|
|
230
|
+
if (isArgumentElement(el) && blocklist.includes(Element.argument)) errors.push(getMessage(Element.argument));
|
|
231
|
+
if (isNumberElement(el) && blocklist.includes(Element.number)) errors.push(getMessage(Element.number));
|
|
232
|
+
if (isDateElement(el) && blocklist.includes(Element.date)) errors.push(getMessage(Element.date));
|
|
233
|
+
if (isTimeElement(el) && blocklist.includes(Element.time)) errors.push(getMessage(Element.time));
|
|
234
|
+
if (isSelectElement(el) && blocklist.includes(Element.select)) errors.push(getMessage(Element.select));
|
|
235
|
+
if (isTagElement(el) && blocklist.includes(Element.tag)) errors.push(getMessage(Element.tag));
|
|
236
|
+
if (isPluralElement(el)) {
|
|
237
|
+
if (blocklist.includes(Element.plural)) errors.push(getMessage(Element.argument));
|
|
238
|
+
if (el.pluralType === "ordinal" && blocklist.includes(Element.selectordinal)) errors.push(getMessage(Element.selectordinal));
|
|
239
|
+
}
|
|
240
|
+
if (isSelectElement(el) || isPluralElement(el)) {
|
|
241
|
+
const { options } = el;
|
|
242
|
+
for (const selector of Object.keys(options)) verifyAst$7(blocklist, options[selector].value);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return errors;
|
|
246
|
+
}
|
|
247
|
+
function checkNode$17(context, node) {
|
|
248
|
+
const settings = getSettings(context);
|
|
249
|
+
const msgs = extractMessages(node, settings);
|
|
250
|
+
if (!msgs.length) return;
|
|
251
|
+
const blocklist = context.options[0];
|
|
252
|
+
if (!Array.isArray(blocklist) || !blocklist.length) return;
|
|
253
|
+
for (const [{ message: { defaultMessage }, messageNode }] of msgs) {
|
|
254
|
+
if (!defaultMessage || !messageNode) continue;
|
|
255
|
+
let ast;
|
|
256
|
+
try {
|
|
257
|
+
ast = parse(defaultMessage, { ignoreTag: settings.ignoreTag });
|
|
258
|
+
} catch (e) {
|
|
259
|
+
context.report({
|
|
260
|
+
node: messageNode,
|
|
261
|
+
messageId: "parseError",
|
|
262
|
+
data: { error: e.message }
|
|
263
|
+
});
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
const errors = verifyAst$7(blocklist, ast);
|
|
267
|
+
for (const error of errors) context.report({
|
|
268
|
+
node: messageNode,
|
|
269
|
+
...error
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
const rule$20 = {
|
|
274
|
+
meta: {
|
|
275
|
+
type: "problem",
|
|
276
|
+
docs: {
|
|
277
|
+
description: "Disallow specific elements in ICU message format",
|
|
278
|
+
url: "https://formatjs.github.io/docs/tooling/linter#blocklist-elements"
|
|
279
|
+
},
|
|
280
|
+
fixable: "code",
|
|
281
|
+
schema: [{
|
|
282
|
+
type: "array",
|
|
283
|
+
items: {
|
|
284
|
+
type: "string",
|
|
285
|
+
enum: Object.keys(Element)
|
|
286
|
+
}
|
|
287
|
+
}],
|
|
288
|
+
messages: {
|
|
289
|
+
...CORE_MESSAGES,
|
|
290
|
+
blocklist: `{{type}} element is blocklisted`
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
create(context) {
|
|
294
|
+
const callExpressionVisitor = (node) => checkNode$17(context, node);
|
|
295
|
+
const parserServices = context.sourceCode.parserServices;
|
|
296
|
+
if (parserServices?.defineTemplateBodyVisitor) return parserServices.defineTemplateBodyVisitor({ CallExpression: callExpressionVisitor }, { CallExpression: callExpressionVisitor });
|
|
297
|
+
return {
|
|
298
|
+
JSXOpeningElement: (node) => checkNode$17(context, node),
|
|
299
|
+
CallExpression: callExpressionVisitor
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
//#endregion
|
|
304
|
+
//#region packages/eslint-plugin-formatjs/rules/enforce-default-message.ts
|
|
305
|
+
let Option$1 = /* @__PURE__ */ function(Option) {
|
|
306
|
+
Option["literal"] = "literal";
|
|
307
|
+
Option["anything"] = "anything";
|
|
308
|
+
return Option;
|
|
309
|
+
}({});
|
|
310
|
+
const name$21 = "enforce-default-message";
|
|
311
|
+
function checkNode$16(context, node) {
|
|
312
|
+
const msgs = extractMessages(node, getSettings(context));
|
|
313
|
+
const { options: [type] } = context;
|
|
314
|
+
for (const [{ message: { defaultMessage }, messageNode, messageDescriptorNode }] of msgs) if (!defaultMessage) {
|
|
315
|
+
if (type === "literal" && messageNode) context.report({
|
|
316
|
+
node: messageNode,
|
|
317
|
+
messageId: "defaultMessageLiteral"
|
|
318
|
+
});
|
|
319
|
+
else if (!messageNode) context.report({
|
|
320
|
+
node: messageDescriptorNode,
|
|
321
|
+
messageId: "defaultMessage"
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
const rule$19 = {
|
|
326
|
+
meta: {
|
|
327
|
+
type: "problem",
|
|
328
|
+
docs: {
|
|
329
|
+
description: "Enforce defaultMessage in message descriptor",
|
|
330
|
+
url: "https://formatjs.github.io/docs/tooling/linter#enforce-default-message"
|
|
331
|
+
},
|
|
332
|
+
fixable: "code",
|
|
333
|
+
schema: [{
|
|
334
|
+
type: "string",
|
|
335
|
+
enum: Object.keys(Option$1)
|
|
336
|
+
}],
|
|
337
|
+
messages: {
|
|
338
|
+
defaultMessageLiteral: `"defaultMessage" must be:
|
|
339
|
+
- a string literal or
|
|
340
|
+
- template literal without variable`,
|
|
341
|
+
defaultMessage: "`defaultMessage` has to be specified in message descriptor"
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
create(context) {
|
|
345
|
+
const callExpressionVisitor = (node) => checkNode$16(context, node);
|
|
346
|
+
const parserServices = context.sourceCode.parserServices;
|
|
347
|
+
if (parserServices?.defineTemplateBodyVisitor) return parserServices.defineTemplateBodyVisitor({ CallExpression: callExpressionVisitor }, { CallExpression: callExpressionVisitor });
|
|
348
|
+
return {
|
|
349
|
+
JSXOpeningElement: (node) => checkNode$16(context, node),
|
|
350
|
+
CallExpression: callExpressionVisitor
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
//#endregion
|
|
355
|
+
//#region packages/eslint-plugin-formatjs/rules/enforce-description.ts
|
|
356
|
+
let Option = /* @__PURE__ */ function(Option) {
|
|
357
|
+
Option["literal"] = "literal";
|
|
358
|
+
Option["anything"] = "anything";
|
|
359
|
+
return Option;
|
|
360
|
+
}({});
|
|
361
|
+
function normalizeOptions(raw) {
|
|
362
|
+
if (typeof raw === "string") return {
|
|
363
|
+
mode: raw,
|
|
364
|
+
minLength: void 0
|
|
365
|
+
};
|
|
366
|
+
if (typeof raw === "object" && raw !== null) return {
|
|
367
|
+
mode: raw.mode,
|
|
368
|
+
minLength: raw.minLength
|
|
369
|
+
};
|
|
370
|
+
return {
|
|
371
|
+
mode: void 0,
|
|
372
|
+
minLength: void 0
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
function checkNode$15(context, node) {
|
|
376
|
+
const msgs = extractMessages(node, getSettings(context));
|
|
377
|
+
const { mode: type, minLength } = normalizeOptions(context.options[0]);
|
|
378
|
+
for (const [{ message: { description }, descriptionNode, messageDescriptorNode }] of msgs) if (!description) {
|
|
379
|
+
if (type === "literal" && descriptionNode) context.report({
|
|
380
|
+
node: descriptionNode,
|
|
381
|
+
messageId: "enforceDescriptionLiteral"
|
|
382
|
+
});
|
|
383
|
+
else if (!descriptionNode) context.report({
|
|
384
|
+
node: messageDescriptorNode,
|
|
385
|
+
messageId: "enforceDescription"
|
|
386
|
+
});
|
|
387
|
+
} else if (typeof minLength === "number" && typeof description === "string" && description.length < minLength) context.report({
|
|
388
|
+
node: descriptionNode ?? messageDescriptorNode,
|
|
389
|
+
messageId: "enforceDescriptionMinLength",
|
|
390
|
+
data: {
|
|
391
|
+
minLength: String(minLength),
|
|
392
|
+
length: String(description.length)
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
const name$20 = "enforce-description";
|
|
397
|
+
const rule$18 = {
|
|
398
|
+
meta: {
|
|
399
|
+
type: "problem",
|
|
400
|
+
docs: {
|
|
401
|
+
description: "Enforce description in message descriptor",
|
|
402
|
+
url: "https://formatjs.github.io/docs/tooling/linter#enforce-description"
|
|
403
|
+
},
|
|
404
|
+
fixable: "code",
|
|
405
|
+
schema: [{ oneOf: [{
|
|
406
|
+
type: "string",
|
|
407
|
+
enum: Object.keys(Option)
|
|
408
|
+
}, {
|
|
409
|
+
type: "object",
|
|
410
|
+
properties: {
|
|
411
|
+
mode: {
|
|
412
|
+
type: "string",
|
|
413
|
+
enum: Object.keys(Option)
|
|
414
|
+
},
|
|
415
|
+
minLength: {
|
|
416
|
+
type: "integer",
|
|
417
|
+
minimum: 1
|
|
418
|
+
}
|
|
419
|
+
},
|
|
420
|
+
additionalProperties: false,
|
|
421
|
+
minProperties: 1
|
|
422
|
+
}] }],
|
|
423
|
+
messages: {
|
|
424
|
+
enforceDescription: "`description` has to be specified in message descriptor",
|
|
425
|
+
enforceDescriptionLiteral: "`description` has to be a string literal (not function call or variable)",
|
|
426
|
+
enforceDescriptionMinLength: "`description` must be at least {{minLength}} characters long (currently {{length}})"
|
|
427
|
+
}
|
|
428
|
+
},
|
|
429
|
+
create(context) {
|
|
430
|
+
const callExpressionVisitor = (node) => checkNode$15(context, node);
|
|
431
|
+
const parserServices = context.sourceCode.parserServices;
|
|
432
|
+
if (parserServices?.defineTemplateBodyVisitor) return parserServices.defineTemplateBodyVisitor({ CallExpression: callExpressionVisitor }, { CallExpression: callExpressionVisitor });
|
|
433
|
+
return {
|
|
434
|
+
JSXOpeningElement: (node) => checkNode$15(context, node),
|
|
435
|
+
CallExpression: callExpressionVisitor
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
//#endregion
|
|
440
|
+
//#region packages/eslint-plugin-formatjs/rules/enforce-id.ts
|
|
441
|
+
function checkNode$14(context, node, { idInterpolationPattern, idWhitelistRegexps, quoteStyle }) {
|
|
442
|
+
const msgs = extractMessages(node, getSettings(context));
|
|
443
|
+
for (const [{ message: { defaultMessage, description, id }, idPropNode, descriptionNode, messagePropNode, messageDescriptorNode }] of msgs) {
|
|
444
|
+
if (!idInterpolationPattern && !idPropNode) {
|
|
445
|
+
context.report({
|
|
446
|
+
node: messageDescriptorNode ?? node,
|
|
447
|
+
messageId: "enforceId"
|
|
448
|
+
});
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
if (idInterpolationPattern) if (!defaultMessage) context.report({
|
|
452
|
+
node: messageDescriptorNode ?? node,
|
|
453
|
+
messageId: "enforceIdDefaultMessage"
|
|
454
|
+
});
|
|
455
|
+
else if (!description && descriptionNode) context.report({
|
|
456
|
+
node: messageDescriptorNode ?? node,
|
|
457
|
+
messageId: "enforceIdDescription"
|
|
458
|
+
});
|
|
459
|
+
else {
|
|
460
|
+
if (idWhitelistRegexps && id && idWhitelistRegexps.some((r) => r.test(id))) continue;
|
|
461
|
+
const correctId = interpolateName({ resourcePath: context.filename }, idInterpolationPattern, { content: description ? `${defaultMessage}#${description}` : defaultMessage });
|
|
462
|
+
if (id !== correctId) {
|
|
463
|
+
let messageId = "enforceIdMatching";
|
|
464
|
+
let messageData = {
|
|
465
|
+
idInterpolationPattern,
|
|
466
|
+
expected: correctId,
|
|
467
|
+
actual: id
|
|
468
|
+
};
|
|
469
|
+
if (idWhitelistRegexps) {
|
|
470
|
+
messageId = "enforceIdMatchingAllowlisted";
|
|
471
|
+
messageData = {
|
|
472
|
+
...messageData,
|
|
473
|
+
idWhitelist: idWhitelistRegexps.map((r) => `"${r.toString()}"`).join(", ")
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
const quote = quoteStyle === "double" ? "\"" : "'";
|
|
477
|
+
context.report({
|
|
478
|
+
node: idPropNode ?? messageDescriptorNode ?? node,
|
|
479
|
+
messageId,
|
|
480
|
+
data: messageData,
|
|
481
|
+
fix(fixer) {
|
|
482
|
+
if (idPropNode) {
|
|
483
|
+
if (idPropNode.type === "JSXAttribute") return fixer.replaceText(idPropNode, `id="${correctId}"`);
|
|
484
|
+
return fixer.replaceText(idPropNode, `id: ${quote}${correctId}${quote}`);
|
|
485
|
+
}
|
|
486
|
+
if (messagePropNode) {
|
|
487
|
+
if (messagePropNode.type === "JSXAttribute") return fixer.insertTextAfter(messagePropNode, ` id="${correctId}"`);
|
|
488
|
+
return fixer.insertTextAfter(messagePropNode, `, id: ${quote}${correctId}${quote}`);
|
|
489
|
+
}
|
|
490
|
+
return null;
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
const name$19 = "enforce-id";
|
|
498
|
+
const rule$17 = {
|
|
499
|
+
meta: {
|
|
500
|
+
type: "problem",
|
|
501
|
+
docs: {
|
|
502
|
+
description: "Enforce (generated) ID in message descriptor",
|
|
503
|
+
url: "https://formatjs.github.io/docs/tooling/linter#enforce-id"
|
|
504
|
+
},
|
|
505
|
+
fixable: "code",
|
|
506
|
+
schema: [{
|
|
507
|
+
type: "object",
|
|
508
|
+
properties: {
|
|
509
|
+
idInterpolationPattern: {
|
|
510
|
+
type: "string",
|
|
511
|
+
description: "Pattern to verify ID against. Recommended value: [sha512:contenthash:base64:6]"
|
|
512
|
+
},
|
|
513
|
+
idWhitelist: {
|
|
514
|
+
type: "array",
|
|
515
|
+
description: "An array of strings with regular expressions. This array allows allowlist custom ids for messages. For example '`\\\\.`' allows any id which has dot; `'^payment_.*'` - allows any custom id which has prefix `payment_`. Be aware that any backslash \\ provided via string must be escaped with an additional backslash.",
|
|
516
|
+
items: { type: "string" }
|
|
517
|
+
},
|
|
518
|
+
quoteStyle: {
|
|
519
|
+
type: "string",
|
|
520
|
+
enum: ["single", "double"],
|
|
521
|
+
description: "Quote style for generated IDs. Defaults to single quotes."
|
|
522
|
+
}
|
|
523
|
+
},
|
|
524
|
+
required: ["idInterpolationPattern"],
|
|
525
|
+
additionalProperties: false
|
|
526
|
+
}],
|
|
527
|
+
messages: {
|
|
528
|
+
enforceId: `id must be specified`,
|
|
529
|
+
enforceIdDefaultMessage: `defaultMessage must be a string literal to calculate generated IDs`,
|
|
530
|
+
enforceIdDescription: `description must be a string literal to calculate generated IDs`,
|
|
531
|
+
enforceIdMatching: `"id" does not match with hash pattern {{idInterpolationPattern}}.
|
|
532
|
+
Expected: {{expected}}
|
|
533
|
+
Actual: {{actual}}`,
|
|
534
|
+
enforceIdMatchingAllowlisted: `"id" does not match with hash pattern {{idInterpolationPattern}} or allowlisted patterns {{idWhitelist}}.
|
|
535
|
+
Expected: {{expected}}
|
|
536
|
+
Actual: {{actual}}`
|
|
537
|
+
}
|
|
538
|
+
},
|
|
539
|
+
create(context) {
|
|
540
|
+
const tmp = context.options[0];
|
|
541
|
+
let opts = {
|
|
542
|
+
idInterpolationPattern: tmp?.idInterpolationPattern,
|
|
543
|
+
quoteStyle: tmp?.quoteStyle || "single"
|
|
544
|
+
};
|
|
545
|
+
if (Array.isArray(tmp?.idWhitelist)) {
|
|
546
|
+
const { idWhitelist } = tmp;
|
|
547
|
+
opts.idWhitelistRegexps = idWhitelist.map((str) => new RegExp(str, "i"));
|
|
548
|
+
}
|
|
549
|
+
const callExpressionVisitor = (node) => checkNode$14(context, node, opts);
|
|
550
|
+
const parserServices = context.sourceCode.parserServices;
|
|
551
|
+
if (parserServices?.defineTemplateBodyVisitor) return parserServices.defineTemplateBodyVisitor({ CallExpression: callExpressionVisitor }, { CallExpression: callExpressionVisitor });
|
|
552
|
+
return {
|
|
553
|
+
JSXOpeningElement: (node) => checkNode$14(context, node, opts),
|
|
554
|
+
CallExpression: callExpressionVisitor
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
//#endregion
|
|
559
|
+
//#region packages/eslint-plugin-formatjs/rules/enforce-placeholders.ts
|
|
560
|
+
function collectPlaceholderNames(ast) {
|
|
561
|
+
const placeholderNames = /* @__PURE__ */ new Set();
|
|
562
|
+
_traverse(ast);
|
|
563
|
+
return placeholderNames;
|
|
564
|
+
function _traverse(ast) {
|
|
565
|
+
for (const element of ast) switch (element.type) {
|
|
566
|
+
case TYPE.literal:
|
|
567
|
+
case TYPE.pound: break;
|
|
568
|
+
case TYPE.tag:
|
|
569
|
+
placeholderNames.add(element.value);
|
|
570
|
+
_traverse(element.children);
|
|
571
|
+
break;
|
|
572
|
+
case TYPE.plural:
|
|
573
|
+
case TYPE.select:
|
|
574
|
+
placeholderNames.add(element.value);
|
|
575
|
+
for (const { value } of Object.values(element.options)) _traverse(value);
|
|
576
|
+
break;
|
|
577
|
+
default:
|
|
578
|
+
placeholderNames.add(element.value);
|
|
579
|
+
break;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
function checkNode$13(context, node) {
|
|
584
|
+
const settings = getSettings(context);
|
|
585
|
+
const msgs = extractMessages(node, {
|
|
586
|
+
excludeMessageDeclCalls: true,
|
|
587
|
+
...settings
|
|
588
|
+
});
|
|
589
|
+
const { options: [opt] } = context;
|
|
590
|
+
const ignoreList = new Set(opt?.ignoreList || []);
|
|
591
|
+
for (const [{ message: { defaultMessage }, messageNode }, values] of msgs) {
|
|
592
|
+
if (!defaultMessage || !messageNode) continue;
|
|
593
|
+
if (values && values.type !== "ObjectExpression") continue;
|
|
594
|
+
if (values?.properties.find((prop) => prop.type === "SpreadElement")) continue;
|
|
595
|
+
const literalElementByLiteralKey = /* @__PURE__ */ new Map();
|
|
596
|
+
if (values) {
|
|
597
|
+
for (const prop of values.properties) if (prop.type === "Property" && !prop.computed) {
|
|
598
|
+
const name = prop.key.type === "Identifier" ? prop.key.name : String(prop.key.value);
|
|
599
|
+
literalElementByLiteralKey.set(name, prop);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
let ast;
|
|
603
|
+
try {
|
|
604
|
+
ast = parse(defaultMessage, { ignoreTag: settings.ignoreTag });
|
|
605
|
+
} catch (e) {
|
|
606
|
+
context.report({
|
|
607
|
+
node: messageNode,
|
|
608
|
+
messageId: "parseError",
|
|
609
|
+
data: { error: e instanceof Error ? e.message : String(e) }
|
|
610
|
+
});
|
|
611
|
+
continue;
|
|
612
|
+
}
|
|
613
|
+
const placeholderNames = collectPlaceholderNames(ast);
|
|
614
|
+
const missingPlaceholders = [];
|
|
615
|
+
placeholderNames.forEach((name) => {
|
|
616
|
+
if (!ignoreList.has(name) && !literalElementByLiteralKey.has(name)) missingPlaceholders.push(name);
|
|
617
|
+
});
|
|
618
|
+
if (missingPlaceholders.length > 0) context.report({
|
|
619
|
+
node: messageNode,
|
|
620
|
+
messageId: "missingValue",
|
|
621
|
+
data: { list: missingPlaceholders.join(", ") }
|
|
622
|
+
});
|
|
623
|
+
literalElementByLiteralKey.forEach((element, key) => {
|
|
624
|
+
if (!ignoreList.has(key) && !placeholderNames.has(key)) context.report({
|
|
625
|
+
node: element,
|
|
626
|
+
messageId: "unusedValue"
|
|
627
|
+
});
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
const name$18 = "enforce-placeholders";
|
|
632
|
+
const rule$16 = {
|
|
633
|
+
meta: {
|
|
634
|
+
type: "problem",
|
|
635
|
+
docs: {
|
|
636
|
+
description: "Enforce that all messages with placeholders have enough passed-in values",
|
|
637
|
+
url: "https://formatjs.github.io/docs/tooling/linter#enforce-placeholders"
|
|
638
|
+
},
|
|
639
|
+
schema: [{
|
|
640
|
+
type: "object",
|
|
641
|
+
properties: { ignoreList: {
|
|
642
|
+
type: "array",
|
|
643
|
+
items: { type: "string" }
|
|
644
|
+
} },
|
|
645
|
+
additionalProperties: false
|
|
646
|
+
}],
|
|
647
|
+
messages: {
|
|
648
|
+
...CORE_MESSAGES,
|
|
649
|
+
missingValue: "Missing value(s) for the following placeholder(s): {{list}}.",
|
|
650
|
+
unusedValue: "Value not used by the message."
|
|
651
|
+
}
|
|
652
|
+
},
|
|
653
|
+
create(context) {
|
|
654
|
+
const callExpressionVisitor = (node) => checkNode$13(context, node);
|
|
655
|
+
const parserServices = context.sourceCode.parserServices;
|
|
656
|
+
if (parserServices?.defineTemplateBodyVisitor) return parserServices.defineTemplateBodyVisitor({ CallExpression: callExpressionVisitor }, { CallExpression: callExpressionVisitor });
|
|
657
|
+
return {
|
|
658
|
+
JSXOpeningElement: (node) => checkNode$13(context, node),
|
|
659
|
+
CallExpression: callExpressionVisitor
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
//#endregion
|
|
664
|
+
//#region packages/eslint-plugin-formatjs/rules/enforce-plural-rules.ts
|
|
665
|
+
var LDML = /* @__PURE__ */ function(LDML) {
|
|
666
|
+
LDML["zero"] = "zero";
|
|
667
|
+
LDML["one"] = "one";
|
|
668
|
+
LDML["two"] = "two";
|
|
669
|
+
LDML["few"] = "few";
|
|
670
|
+
LDML["many"] = "many";
|
|
671
|
+
LDML["other"] = "other";
|
|
672
|
+
return LDML;
|
|
673
|
+
}(LDML || {});
|
|
674
|
+
function verifyAst$6(plConfig, ast) {
|
|
675
|
+
const errors = [];
|
|
676
|
+
for (const el of ast) if (isPluralElement(el)) {
|
|
677
|
+
const rules = Object.keys(plConfig);
|
|
678
|
+
for (const rule of rules) {
|
|
679
|
+
if (plConfig[rule] && !el.options[rule]) errors.push({
|
|
680
|
+
messageId: "missingPlural",
|
|
681
|
+
data: { rule }
|
|
682
|
+
});
|
|
683
|
+
if (!plConfig[rule] && el.options[rule]) errors.push({
|
|
684
|
+
messageId: "forbidden",
|
|
685
|
+
data: { rule }
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
const { options } = el;
|
|
689
|
+
for (const selector of Object.keys(options)) errors.push(...verifyAst$6(plConfig, options[selector].value));
|
|
690
|
+
}
|
|
691
|
+
return errors;
|
|
692
|
+
}
|
|
693
|
+
function checkNode$12(context, node) {
|
|
694
|
+
const settings = getSettings(context);
|
|
695
|
+
const msgs = extractMessages(node, settings);
|
|
696
|
+
if (!msgs.length) return;
|
|
697
|
+
const plConfig = context.options[0];
|
|
698
|
+
if (!plConfig) return;
|
|
699
|
+
for (const [{ message: { defaultMessage }, messageNode }] of msgs) {
|
|
700
|
+
if (!defaultMessage || !messageNode) continue;
|
|
701
|
+
let ast;
|
|
702
|
+
try {
|
|
703
|
+
ast = parse(defaultMessage, { ignoreTag: settings.ignoreTag });
|
|
704
|
+
} catch (e) {
|
|
705
|
+
context.report({
|
|
706
|
+
node: messageNode,
|
|
707
|
+
messageId: "parseError",
|
|
708
|
+
data: { error: e.message }
|
|
709
|
+
});
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
const errors = verifyAst$6(plConfig, ast);
|
|
713
|
+
for (const error of errors) context.report({
|
|
714
|
+
node: messageNode,
|
|
715
|
+
...error
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
const name$17 = "enforce-plural-rules";
|
|
720
|
+
const rule$15 = {
|
|
721
|
+
meta: {
|
|
722
|
+
type: "problem",
|
|
723
|
+
docs: {
|
|
724
|
+
description: "Enforce plural rules to always specify certain categories like `one`/`other`",
|
|
725
|
+
url: "https://formatjs.github.io/docs/tooling/linter#enforce-plural-rules"
|
|
726
|
+
},
|
|
727
|
+
fixable: "code",
|
|
728
|
+
schema: [{
|
|
729
|
+
type: "object",
|
|
730
|
+
properties: Object.keys(LDML).reduce((schema, k) => {
|
|
731
|
+
schema[k] = { type: "boolean" };
|
|
732
|
+
return schema;
|
|
733
|
+
}, {}),
|
|
734
|
+
additionalProperties: false
|
|
735
|
+
}],
|
|
736
|
+
messages: {
|
|
737
|
+
...CORE_MESSAGES,
|
|
738
|
+
missingPlural: `Missing plural rule "{{rule}}"`,
|
|
739
|
+
forbidden: `Plural rule "{{rule}}" is forbidden`
|
|
740
|
+
}
|
|
741
|
+
},
|
|
742
|
+
create(context) {
|
|
743
|
+
const callExpressionVisitor = (node) => checkNode$12(context, node);
|
|
744
|
+
const parserServices = context.sourceCode.parserServices;
|
|
745
|
+
if (parserServices?.defineTemplateBodyVisitor) return parserServices.defineTemplateBodyVisitor({ CallExpression: callExpressionVisitor }, { CallExpression: callExpressionVisitor });
|
|
746
|
+
return {
|
|
747
|
+
JSXOpeningElement: (node) => checkNode$12(context, node),
|
|
748
|
+
CallExpression: callExpressionVisitor
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
};
|
|
752
|
+
//#endregion
|
|
753
|
+
//#region packages/eslint-plugin-formatjs/rules/no-camel-case.ts
|
|
754
|
+
const name$16 = "no-camel-case";
|
|
755
|
+
const CAMEL_CASE_REGEX = /[A-Z]/;
|
|
756
|
+
function verifyAst$5(ast) {
|
|
757
|
+
const errors = [];
|
|
758
|
+
for (const el of ast) {
|
|
759
|
+
if (isArgumentElement(el)) {
|
|
760
|
+
if (CAMEL_CASE_REGEX.test(el.value)) errors.push({
|
|
761
|
+
messageId: "camelcase",
|
|
762
|
+
data: {}
|
|
763
|
+
});
|
|
764
|
+
continue;
|
|
765
|
+
}
|
|
766
|
+
if (isPluralElement(el)) {
|
|
767
|
+
if (CAMEL_CASE_REGEX.test(el.value)) errors.push({
|
|
768
|
+
messageId: "camelcase",
|
|
769
|
+
data: {}
|
|
770
|
+
});
|
|
771
|
+
const { options } = el;
|
|
772
|
+
for (const selector of Object.keys(options)) errors.push(...verifyAst$5(options[selector].value));
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
return errors;
|
|
776
|
+
}
|
|
777
|
+
function checkNode$11(context, node) {
|
|
778
|
+
const settings = getSettings(context);
|
|
779
|
+
const msgs = extractMessages(node, settings);
|
|
780
|
+
for (const [{ message: { defaultMessage }, messageNode }] of msgs) {
|
|
781
|
+
if (!defaultMessage || !messageNode) continue;
|
|
782
|
+
let ast;
|
|
783
|
+
try {
|
|
784
|
+
ast = parse(defaultMessage, { ignoreTag: settings.ignoreTag });
|
|
785
|
+
} catch (e) {
|
|
786
|
+
context.report({
|
|
787
|
+
node: messageNode,
|
|
788
|
+
messageId: "parseError",
|
|
789
|
+
data: { error: e.message }
|
|
790
|
+
});
|
|
791
|
+
continue;
|
|
792
|
+
}
|
|
793
|
+
const errors = verifyAst$5(ast);
|
|
794
|
+
for (const error of errors) context.report({
|
|
795
|
+
node: messageNode,
|
|
796
|
+
...error
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
const rule$14 = {
|
|
801
|
+
meta: {
|
|
802
|
+
type: "problem",
|
|
803
|
+
docs: {
|
|
804
|
+
description: "Disallow camel case placeholders in message",
|
|
805
|
+
url: "https://formatjs.github.io/docs/tooling/linter#no-camel-case"
|
|
806
|
+
},
|
|
807
|
+
fixable: "code",
|
|
808
|
+
schema: [],
|
|
809
|
+
messages: {
|
|
810
|
+
...CORE_MESSAGES,
|
|
811
|
+
camelcase: "Camel case arguments are not allowed"
|
|
812
|
+
}
|
|
813
|
+
},
|
|
814
|
+
create(context) {
|
|
815
|
+
const callExpressionVisitor = (node) => checkNode$11(context, node);
|
|
816
|
+
const parserServices = context.sourceCode.parserServices;
|
|
817
|
+
if (parserServices?.defineTemplateBodyVisitor) return parserServices.defineTemplateBodyVisitor({ CallExpression: callExpressionVisitor }, { CallExpression: callExpressionVisitor });
|
|
818
|
+
return {
|
|
819
|
+
JSXOpeningElement: (node) => checkNode$11(context, node),
|
|
820
|
+
CallExpression: callExpressionVisitor
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
};
|
|
824
|
+
//#endregion
|
|
825
|
+
//#region packages/eslint-plugin-formatjs/rules/no-complex-selectors.ts
|
|
826
|
+
function calculateComplexity(ast) {
|
|
827
|
+
const complexityByNode = /* @__PURE__ */ new Map();
|
|
828
|
+
return _calculate(ast, 0);
|
|
829
|
+
function _calculate(ast, index) {
|
|
830
|
+
const element = ast[index];
|
|
831
|
+
if (!element) return 1;
|
|
832
|
+
const cachedComplexity = complexityByNode.get(element);
|
|
833
|
+
if (cachedComplexity !== void 0) return cachedComplexity;
|
|
834
|
+
let complexity;
|
|
835
|
+
switch (element.type) {
|
|
836
|
+
case TYPE.plural:
|
|
837
|
+
case TYPE.select: {
|
|
838
|
+
let sumOfOptions = 0;
|
|
839
|
+
for (const { value } of Object.values(element.options)) sumOfOptions += _calculate(value, 0);
|
|
840
|
+
complexity = sumOfOptions * _calculate(ast, index + 1);
|
|
841
|
+
break;
|
|
842
|
+
}
|
|
843
|
+
case TYPE.tag:
|
|
844
|
+
complexity = _calculate(element.children, 0) * _calculate(ast, index + 1);
|
|
845
|
+
break;
|
|
846
|
+
default:
|
|
847
|
+
complexity = _calculate(ast, index + 1);
|
|
848
|
+
break;
|
|
849
|
+
}
|
|
850
|
+
complexityByNode.set(element, complexity);
|
|
851
|
+
return complexity;
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
function checkNode$10(context, node) {
|
|
855
|
+
const settings = getSettings(context);
|
|
856
|
+
const msgs = extractMessages(node, settings);
|
|
857
|
+
if (!msgs.length) return;
|
|
858
|
+
const config = {
|
|
859
|
+
limit: 20,
|
|
860
|
+
...context.options[0]
|
|
861
|
+
};
|
|
862
|
+
for (const [{ message: { defaultMessage }, messageNode }] of msgs) {
|
|
863
|
+
if (!defaultMessage || !messageNode) continue;
|
|
864
|
+
let ast;
|
|
865
|
+
try {
|
|
866
|
+
ast = parse(defaultMessage, { ignoreTag: settings.ignoreTag });
|
|
867
|
+
} catch (e) {
|
|
868
|
+
context.report({
|
|
869
|
+
node: messageNode,
|
|
870
|
+
messageId: "parseError",
|
|
871
|
+
data: { error: e instanceof Error ? e.message : String(e) }
|
|
872
|
+
});
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
const complexity = calculateComplexity(ast);
|
|
876
|
+
if (complexity > config.limit) context.report({
|
|
877
|
+
node: messageNode,
|
|
878
|
+
messageId: "tooComplex",
|
|
879
|
+
data: {
|
|
880
|
+
complexity,
|
|
881
|
+
limit: config.limit
|
|
882
|
+
}
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
const name$15 = "no-complex-selectors";
|
|
887
|
+
const rule$13 = {
|
|
888
|
+
meta: {
|
|
889
|
+
type: "problem",
|
|
890
|
+
docs: {
|
|
891
|
+
description: `Make sure a sentence is not too complex.
|
|
892
|
+
Complexity is determined by how many strings are produced when we try to
|
|
893
|
+
flatten the sentence given its selectors. For example:
|
|
894
|
+
"I have {count, plural, one{a dog} other{many dogs}}"
|
|
895
|
+
has the complexity of 2 because flattening the plural selector
|
|
896
|
+
results in 2 sentences: "I have a dog" & "I have many dogs".
|
|
897
|
+
Default complexity limit is 20
|
|
898
|
+
(using Smartling as a reference: https://help.smartling.com/hc/en-us/articles/360008030994-ICU-MessageFormat)
|
|
899
|
+
`,
|
|
900
|
+
url: "https://formatjs.github.io/docs/tooling/linter#no-complex-selectors"
|
|
901
|
+
},
|
|
902
|
+
schema: [{
|
|
903
|
+
type: "object",
|
|
904
|
+
properties: { limit: { type: "number" } },
|
|
905
|
+
additionalProperties: false
|
|
906
|
+
}],
|
|
907
|
+
fixable: "code",
|
|
908
|
+
messages: {
|
|
909
|
+
...CORE_MESSAGES,
|
|
910
|
+
tooComplex: `Message complexity is too high ({{complexity}} vs limit at {{limit}})`
|
|
911
|
+
}
|
|
912
|
+
},
|
|
913
|
+
create(context) {
|
|
914
|
+
const callExpressionVisitor = (node) => checkNode$10(context, node);
|
|
915
|
+
const parserServices = context.sourceCode.parserServices;
|
|
916
|
+
if (parserServices?.defineTemplateBodyVisitor) return parserServices.defineTemplateBodyVisitor({ CallExpression: callExpressionVisitor }, { CallExpression: callExpressionVisitor });
|
|
917
|
+
return {
|
|
918
|
+
JSXOpeningElement: (node) => checkNode$10(context, node),
|
|
919
|
+
CallExpression: callExpressionVisitor
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
};
|
|
923
|
+
//#endregion
|
|
924
|
+
//#region packages/eslint-plugin-formatjs/emoji-data.generated.ts
|
|
925
|
+
const EMOJI_VERSIONS = [
|
|
926
|
+
"0.0",
|
|
927
|
+
"0.6",
|
|
928
|
+
"0.7",
|
|
929
|
+
"1.0",
|
|
930
|
+
"2.0",
|
|
931
|
+
"3.0",
|
|
932
|
+
"4.0",
|
|
933
|
+
"5.0",
|
|
934
|
+
"11.0",
|
|
935
|
+
"12.0",
|
|
936
|
+
"13.0",
|
|
937
|
+
"14.0",
|
|
938
|
+
"15.0",
|
|
939
|
+
"16.0",
|
|
940
|
+
"17.0"
|
|
941
|
+
];
|
|
942
|
+
/**
|
|
943
|
+
* Emoji codepoint ranges with their Unicode versions
|
|
944
|
+
* Total ranges: 418
|
|
945
|
+
*
|
|
946
|
+
* To check if a codepoint belongs to a version, iterate through ranges
|
|
947
|
+
* and check if codepoint >= start && codepoint <= end
|
|
948
|
+
*/
|
|
949
|
+
const EMOJI_RANGES = [
|
|
950
|
+
{
|
|
951
|
+
start: 35,
|
|
952
|
+
end: 35,
|
|
953
|
+
version: "0.0",
|
|
954
|
+
name: "hash sign"
|
|
955
|
+
},
|
|
956
|
+
{
|
|
957
|
+
start: 42,
|
|
958
|
+
end: 42,
|
|
959
|
+
version: "0.0",
|
|
960
|
+
name: "asterisk"
|
|
961
|
+
},
|
|
962
|
+
{
|
|
963
|
+
start: 48,
|
|
964
|
+
end: 57,
|
|
965
|
+
version: "0.0",
|
|
966
|
+
name: "digit zero..digit nine"
|
|
967
|
+
},
|
|
968
|
+
{
|
|
969
|
+
start: 127462,
|
|
970
|
+
end: 127487,
|
|
971
|
+
version: "0.0",
|
|
972
|
+
name: "regional indicator symbol letter a..regional indicator symbol letter z"
|
|
973
|
+
},
|
|
974
|
+
{
|
|
975
|
+
start: 169,
|
|
976
|
+
end: 169,
|
|
977
|
+
version: "0.6",
|
|
978
|
+
name: "copyright"
|
|
979
|
+
},
|
|
980
|
+
{
|
|
981
|
+
start: 174,
|
|
982
|
+
end: 174,
|
|
983
|
+
version: "0.6",
|
|
984
|
+
name: "registered"
|
|
985
|
+
},
|
|
986
|
+
{
|
|
987
|
+
start: 8252,
|
|
988
|
+
end: 8252,
|
|
989
|
+
version: "0.6",
|
|
990
|
+
name: "double exclamation mark"
|
|
991
|
+
},
|
|
992
|
+
{
|
|
993
|
+
start: 8265,
|
|
994
|
+
end: 8265,
|
|
995
|
+
version: "0.6",
|
|
996
|
+
name: "exclamation question mark"
|
|
997
|
+
},
|
|
998
|
+
{
|
|
999
|
+
start: 8482,
|
|
1000
|
+
end: 8482,
|
|
1001
|
+
version: "0.6",
|
|
1002
|
+
name: "trade mark"
|
|
1003
|
+
},
|
|
1004
|
+
{
|
|
1005
|
+
start: 8505,
|
|
1006
|
+
end: 8505,
|
|
1007
|
+
version: "0.6",
|
|
1008
|
+
name: "information"
|
|
1009
|
+
},
|
|
1010
|
+
{
|
|
1011
|
+
start: 8596,
|
|
1012
|
+
end: 8601,
|
|
1013
|
+
version: "0.6",
|
|
1014
|
+
name: "left-right arrow..down-left arrow"
|
|
1015
|
+
},
|
|
1016
|
+
{
|
|
1017
|
+
start: 8617,
|
|
1018
|
+
end: 8618,
|
|
1019
|
+
version: "0.6",
|
|
1020
|
+
name: "right arrow curving left..left arrow curving right"
|
|
1021
|
+
},
|
|
1022
|
+
{
|
|
1023
|
+
start: 8986,
|
|
1024
|
+
end: 8987,
|
|
1025
|
+
version: "0.6",
|
|
1026
|
+
name: "watch..hourglass done"
|
|
1027
|
+
},
|
|
1028
|
+
{
|
|
1029
|
+
start: 9193,
|
|
1030
|
+
end: 9196,
|
|
1031
|
+
version: "0.6",
|
|
1032
|
+
name: "fast-forward button..fast down button"
|
|
1033
|
+
},
|
|
1034
|
+
{
|
|
1035
|
+
start: 9200,
|
|
1036
|
+
end: 9200,
|
|
1037
|
+
version: "0.6",
|
|
1038
|
+
name: "alarm clock"
|
|
1039
|
+
},
|
|
1040
|
+
{
|
|
1041
|
+
start: 9203,
|
|
1042
|
+
end: 9203,
|
|
1043
|
+
version: "0.6",
|
|
1044
|
+
name: "hourglass not done"
|
|
1045
|
+
},
|
|
1046
|
+
{
|
|
1047
|
+
start: 9410,
|
|
1048
|
+
end: 9410,
|
|
1049
|
+
version: "0.6",
|
|
1050
|
+
name: "circled M"
|
|
1051
|
+
},
|
|
1052
|
+
{
|
|
1053
|
+
start: 9642,
|
|
1054
|
+
end: 9643,
|
|
1055
|
+
version: "0.6",
|
|
1056
|
+
name: "black small square..white small square"
|
|
1057
|
+
},
|
|
1058
|
+
{
|
|
1059
|
+
start: 9654,
|
|
1060
|
+
end: 9654,
|
|
1061
|
+
version: "0.6",
|
|
1062
|
+
name: "play button"
|
|
1063
|
+
},
|
|
1064
|
+
{
|
|
1065
|
+
start: 9664,
|
|
1066
|
+
end: 9664,
|
|
1067
|
+
version: "0.6",
|
|
1068
|
+
name: "reverse button"
|
|
1069
|
+
},
|
|
1070
|
+
{
|
|
1071
|
+
start: 9723,
|
|
1072
|
+
end: 9726,
|
|
1073
|
+
version: "0.6",
|
|
1074
|
+
name: "white medium square..black medium-small square"
|
|
1075
|
+
},
|
|
1076
|
+
{
|
|
1077
|
+
start: 9728,
|
|
1078
|
+
end: 9729,
|
|
1079
|
+
version: "0.6",
|
|
1080
|
+
name: "sun..cloud"
|
|
1081
|
+
},
|
|
1082
|
+
{
|
|
1083
|
+
start: 9742,
|
|
1084
|
+
end: 9742,
|
|
1085
|
+
version: "0.6",
|
|
1086
|
+
name: "telephone"
|
|
1087
|
+
},
|
|
1088
|
+
{
|
|
1089
|
+
start: 9745,
|
|
1090
|
+
end: 9745,
|
|
1091
|
+
version: "0.6",
|
|
1092
|
+
name: "check box with check"
|
|
1093
|
+
},
|
|
1094
|
+
{
|
|
1095
|
+
start: 9748,
|
|
1096
|
+
end: 9749,
|
|
1097
|
+
version: "0.6",
|
|
1098
|
+
name: "umbrella with rain drops..hot beverage"
|
|
1099
|
+
},
|
|
1100
|
+
{
|
|
1101
|
+
start: 9757,
|
|
1102
|
+
end: 9757,
|
|
1103
|
+
version: "0.6",
|
|
1104
|
+
name: "index pointing up"
|
|
1105
|
+
},
|
|
1106
|
+
{
|
|
1107
|
+
start: 9786,
|
|
1108
|
+
end: 9786,
|
|
1109
|
+
version: "0.6",
|
|
1110
|
+
name: "smiling face"
|
|
1111
|
+
},
|
|
1112
|
+
{
|
|
1113
|
+
start: 9800,
|
|
1114
|
+
end: 9811,
|
|
1115
|
+
version: "0.6",
|
|
1116
|
+
name: "Aries..Pisces"
|
|
1117
|
+
},
|
|
1118
|
+
{
|
|
1119
|
+
start: 9824,
|
|
1120
|
+
end: 9824,
|
|
1121
|
+
version: "0.6",
|
|
1122
|
+
name: "spade suit"
|
|
1123
|
+
},
|
|
1124
|
+
{
|
|
1125
|
+
start: 9827,
|
|
1126
|
+
end: 9827,
|
|
1127
|
+
version: "0.6",
|
|
1128
|
+
name: "club suit"
|
|
1129
|
+
},
|
|
1130
|
+
{
|
|
1131
|
+
start: 9829,
|
|
1132
|
+
end: 9830,
|
|
1133
|
+
version: "0.6",
|
|
1134
|
+
name: "heart suit..diamond suit"
|
|
1135
|
+
},
|
|
1136
|
+
{
|
|
1137
|
+
start: 9832,
|
|
1138
|
+
end: 9832,
|
|
1139
|
+
version: "0.6",
|
|
1140
|
+
name: "hot springs"
|
|
1141
|
+
},
|
|
1142
|
+
{
|
|
1143
|
+
start: 9851,
|
|
1144
|
+
end: 9851,
|
|
1145
|
+
version: "0.6",
|
|
1146
|
+
name: "recycling symbol"
|
|
1147
|
+
},
|
|
1148
|
+
{
|
|
1149
|
+
start: 9855,
|
|
1150
|
+
end: 9855,
|
|
1151
|
+
version: "0.6",
|
|
1152
|
+
name: "wheelchair symbol"
|
|
1153
|
+
},
|
|
1154
|
+
{
|
|
1155
|
+
start: 9875,
|
|
1156
|
+
end: 9875,
|
|
1157
|
+
version: "0.6",
|
|
1158
|
+
name: "anchor"
|
|
1159
|
+
},
|
|
1160
|
+
{
|
|
1161
|
+
start: 9888,
|
|
1162
|
+
end: 9889,
|
|
1163
|
+
version: "0.6",
|
|
1164
|
+
name: "warning..high voltage"
|
|
1165
|
+
},
|
|
1166
|
+
{
|
|
1167
|
+
start: 9898,
|
|
1168
|
+
end: 9899,
|
|
1169
|
+
version: "0.6",
|
|
1170
|
+
name: "white circle..black circle"
|
|
1171
|
+
},
|
|
1172
|
+
{
|
|
1173
|
+
start: 9917,
|
|
1174
|
+
end: 9918,
|
|
1175
|
+
version: "0.6",
|
|
1176
|
+
name: "soccer ball..baseball"
|
|
1177
|
+
},
|
|
1178
|
+
{
|
|
1179
|
+
start: 9924,
|
|
1180
|
+
end: 9925,
|
|
1181
|
+
version: "0.6",
|
|
1182
|
+
name: "snowman without snow..sun behind cloud"
|
|
1183
|
+
},
|
|
1184
|
+
{
|
|
1185
|
+
start: 9934,
|
|
1186
|
+
end: 9934,
|
|
1187
|
+
version: "0.6",
|
|
1188
|
+
name: "Ophiuchus"
|
|
1189
|
+
},
|
|
1190
|
+
{
|
|
1191
|
+
start: 9940,
|
|
1192
|
+
end: 9940,
|
|
1193
|
+
version: "0.6",
|
|
1194
|
+
name: "no entry"
|
|
1195
|
+
},
|
|
1196
|
+
{
|
|
1197
|
+
start: 9962,
|
|
1198
|
+
end: 9962,
|
|
1199
|
+
version: "0.6",
|
|
1200
|
+
name: "church"
|
|
1201
|
+
},
|
|
1202
|
+
{
|
|
1203
|
+
start: 9970,
|
|
1204
|
+
end: 9971,
|
|
1205
|
+
version: "0.6",
|
|
1206
|
+
name: "fountain..flag in hole"
|
|
1207
|
+
},
|
|
1208
|
+
{
|
|
1209
|
+
start: 9973,
|
|
1210
|
+
end: 9973,
|
|
1211
|
+
version: "0.6",
|
|
1212
|
+
name: "sailboat"
|
|
1213
|
+
},
|
|
1214
|
+
{
|
|
1215
|
+
start: 9978,
|
|
1216
|
+
end: 9978,
|
|
1217
|
+
version: "0.6",
|
|
1218
|
+
name: "tent"
|
|
1219
|
+
},
|
|
1220
|
+
{
|
|
1221
|
+
start: 9981,
|
|
1222
|
+
end: 9981,
|
|
1223
|
+
version: "0.6",
|
|
1224
|
+
name: "fuel pump"
|
|
1225
|
+
},
|
|
1226
|
+
{
|
|
1227
|
+
start: 9986,
|
|
1228
|
+
end: 9986,
|
|
1229
|
+
version: "0.6",
|
|
1230
|
+
name: "scissors"
|
|
1231
|
+
},
|
|
1232
|
+
{
|
|
1233
|
+
start: 9989,
|
|
1234
|
+
end: 9989,
|
|
1235
|
+
version: "0.6",
|
|
1236
|
+
name: "check mark button"
|
|
1237
|
+
},
|
|
1238
|
+
{
|
|
1239
|
+
start: 9992,
|
|
1240
|
+
end: 9996,
|
|
1241
|
+
version: "0.6",
|
|
1242
|
+
name: "airplane..victory hand"
|
|
1243
|
+
},
|
|
1244
|
+
{
|
|
1245
|
+
start: 9999,
|
|
1246
|
+
end: 9999,
|
|
1247
|
+
version: "0.6",
|
|
1248
|
+
name: "pencil"
|
|
1249
|
+
},
|
|
1250
|
+
{
|
|
1251
|
+
start: 10002,
|
|
1252
|
+
end: 10002,
|
|
1253
|
+
version: "0.6",
|
|
1254
|
+
name: "black nib"
|
|
1255
|
+
},
|
|
1256
|
+
{
|
|
1257
|
+
start: 10004,
|
|
1258
|
+
end: 10004,
|
|
1259
|
+
version: "0.6",
|
|
1260
|
+
name: "check mark"
|
|
1261
|
+
},
|
|
1262
|
+
{
|
|
1263
|
+
start: 10006,
|
|
1264
|
+
end: 10006,
|
|
1265
|
+
version: "0.6",
|
|
1266
|
+
name: "multiply"
|
|
1267
|
+
},
|
|
1268
|
+
{
|
|
1269
|
+
start: 10024,
|
|
1270
|
+
end: 10024,
|
|
1271
|
+
version: "0.6",
|
|
1272
|
+
name: "sparkles"
|
|
1273
|
+
},
|
|
1274
|
+
{
|
|
1275
|
+
start: 10035,
|
|
1276
|
+
end: 10036,
|
|
1277
|
+
version: "0.6",
|
|
1278
|
+
name: "eight-spoked asterisk..eight-pointed star"
|
|
1279
|
+
},
|
|
1280
|
+
{
|
|
1281
|
+
start: 10052,
|
|
1282
|
+
end: 10052,
|
|
1283
|
+
version: "0.6",
|
|
1284
|
+
name: "snowflake"
|
|
1285
|
+
},
|
|
1286
|
+
{
|
|
1287
|
+
start: 10055,
|
|
1288
|
+
end: 10055,
|
|
1289
|
+
version: "0.6",
|
|
1290
|
+
name: "sparkle"
|
|
1291
|
+
},
|
|
1292
|
+
{
|
|
1293
|
+
start: 10060,
|
|
1294
|
+
end: 10060,
|
|
1295
|
+
version: "0.6",
|
|
1296
|
+
name: "cross mark"
|
|
1297
|
+
},
|
|
1298
|
+
{
|
|
1299
|
+
start: 10062,
|
|
1300
|
+
end: 10062,
|
|
1301
|
+
version: "0.6",
|
|
1302
|
+
name: "cross mark button"
|
|
1303
|
+
},
|
|
1304
|
+
{
|
|
1305
|
+
start: 10067,
|
|
1306
|
+
end: 10069,
|
|
1307
|
+
version: "0.6",
|
|
1308
|
+
name: "red question mark..white exclamation mark"
|
|
1309
|
+
},
|
|
1310
|
+
{
|
|
1311
|
+
start: 10071,
|
|
1312
|
+
end: 10071,
|
|
1313
|
+
version: "0.6",
|
|
1314
|
+
name: "red exclamation mark"
|
|
1315
|
+
},
|
|
1316
|
+
{
|
|
1317
|
+
start: 10084,
|
|
1318
|
+
end: 10084,
|
|
1319
|
+
version: "0.6",
|
|
1320
|
+
name: "red heart"
|
|
1321
|
+
},
|
|
1322
|
+
{
|
|
1323
|
+
start: 10133,
|
|
1324
|
+
end: 10135,
|
|
1325
|
+
version: "0.6",
|
|
1326
|
+
name: "plus..divide"
|
|
1327
|
+
},
|
|
1328
|
+
{
|
|
1329
|
+
start: 10145,
|
|
1330
|
+
end: 10145,
|
|
1331
|
+
version: "0.6",
|
|
1332
|
+
name: "right arrow"
|
|
1333
|
+
},
|
|
1334
|
+
{
|
|
1335
|
+
start: 10160,
|
|
1336
|
+
end: 10160,
|
|
1337
|
+
version: "0.6",
|
|
1338
|
+
name: "curly loop"
|
|
1339
|
+
},
|
|
1340
|
+
{
|
|
1341
|
+
start: 10548,
|
|
1342
|
+
end: 10549,
|
|
1343
|
+
version: "0.6",
|
|
1344
|
+
name: "right arrow curving up..right arrow curving down"
|
|
1345
|
+
},
|
|
1346
|
+
{
|
|
1347
|
+
start: 11013,
|
|
1348
|
+
end: 11015,
|
|
1349
|
+
version: "0.6",
|
|
1350
|
+
name: "left arrow..down arrow"
|
|
1351
|
+
},
|
|
1352
|
+
{
|
|
1353
|
+
start: 11035,
|
|
1354
|
+
end: 11036,
|
|
1355
|
+
version: "0.6",
|
|
1356
|
+
name: "black large square..white large square"
|
|
1357
|
+
},
|
|
1358
|
+
{
|
|
1359
|
+
start: 11088,
|
|
1360
|
+
end: 11088,
|
|
1361
|
+
version: "0.6",
|
|
1362
|
+
name: "star"
|
|
1363
|
+
},
|
|
1364
|
+
{
|
|
1365
|
+
start: 11093,
|
|
1366
|
+
end: 11093,
|
|
1367
|
+
version: "0.6",
|
|
1368
|
+
name: "hollow red circle"
|
|
1369
|
+
},
|
|
1370
|
+
{
|
|
1371
|
+
start: 12336,
|
|
1372
|
+
end: 12336,
|
|
1373
|
+
version: "0.6",
|
|
1374
|
+
name: "wavy dash"
|
|
1375
|
+
},
|
|
1376
|
+
{
|
|
1377
|
+
start: 12349,
|
|
1378
|
+
end: 12349,
|
|
1379
|
+
version: "0.6",
|
|
1380
|
+
name: "part alternation mark"
|
|
1381
|
+
},
|
|
1382
|
+
{
|
|
1383
|
+
start: 12951,
|
|
1384
|
+
end: 12951,
|
|
1385
|
+
version: "0.6",
|
|
1386
|
+
name: "Japanese “congratulations” button"
|
|
1387
|
+
},
|
|
1388
|
+
{
|
|
1389
|
+
start: 12953,
|
|
1390
|
+
end: 12953,
|
|
1391
|
+
version: "0.6",
|
|
1392
|
+
name: "Japanese “secret” button"
|
|
1393
|
+
},
|
|
1394
|
+
{
|
|
1395
|
+
start: 126980,
|
|
1396
|
+
end: 126980,
|
|
1397
|
+
version: "0.6",
|
|
1398
|
+
name: "mahjong red dragon"
|
|
1399
|
+
},
|
|
1400
|
+
{
|
|
1401
|
+
start: 127183,
|
|
1402
|
+
end: 127183,
|
|
1403
|
+
version: "0.6",
|
|
1404
|
+
name: "joker"
|
|
1405
|
+
},
|
|
1406
|
+
{
|
|
1407
|
+
start: 127344,
|
|
1408
|
+
end: 127345,
|
|
1409
|
+
version: "0.6",
|
|
1410
|
+
name: "A button (blood type)..B button (blood type)"
|
|
1411
|
+
},
|
|
1412
|
+
{
|
|
1413
|
+
start: 127358,
|
|
1414
|
+
end: 127359,
|
|
1415
|
+
version: "0.6",
|
|
1416
|
+
name: "O button (blood type)..P button"
|
|
1417
|
+
},
|
|
1418
|
+
{
|
|
1419
|
+
start: 127374,
|
|
1420
|
+
end: 127374,
|
|
1421
|
+
version: "0.6",
|
|
1422
|
+
name: "AB button (blood type)"
|
|
1423
|
+
},
|
|
1424
|
+
{
|
|
1425
|
+
start: 127377,
|
|
1426
|
+
end: 127386,
|
|
1427
|
+
version: "0.6",
|
|
1428
|
+
name: "CL button..VS button"
|
|
1429
|
+
},
|
|
1430
|
+
{
|
|
1431
|
+
start: 127489,
|
|
1432
|
+
end: 127490,
|
|
1433
|
+
version: "0.6",
|
|
1434
|
+
name: "Japanese “here” button..Japanese “service charge” button"
|
|
1435
|
+
},
|
|
1436
|
+
{
|
|
1437
|
+
start: 127514,
|
|
1438
|
+
end: 127514,
|
|
1439
|
+
version: "0.6",
|
|
1440
|
+
name: "Japanese “free of charge” button"
|
|
1441
|
+
},
|
|
1442
|
+
{
|
|
1443
|
+
start: 127535,
|
|
1444
|
+
end: 127535,
|
|
1445
|
+
version: "0.6",
|
|
1446
|
+
name: "Japanese “reserved” button"
|
|
1447
|
+
},
|
|
1448
|
+
{
|
|
1449
|
+
start: 127538,
|
|
1450
|
+
end: 127546,
|
|
1451
|
+
version: "0.6",
|
|
1452
|
+
name: "Japanese “prohibited” button..Japanese “open for business” button"
|
|
1453
|
+
},
|
|
1454
|
+
{
|
|
1455
|
+
start: 127568,
|
|
1456
|
+
end: 127569,
|
|
1457
|
+
version: "0.6",
|
|
1458
|
+
name: "Japanese “bargain” button..Japanese “acceptable” button"
|
|
1459
|
+
},
|
|
1460
|
+
{
|
|
1461
|
+
start: 127744,
|
|
1462
|
+
end: 127756,
|
|
1463
|
+
version: "0.6",
|
|
1464
|
+
name: "cyclone..milky way"
|
|
1465
|
+
},
|
|
1466
|
+
{
|
|
1467
|
+
start: 127759,
|
|
1468
|
+
end: 127759,
|
|
1469
|
+
version: "0.6",
|
|
1470
|
+
name: "globe showing Asia-Australia"
|
|
1471
|
+
},
|
|
1472
|
+
{
|
|
1473
|
+
start: 127761,
|
|
1474
|
+
end: 127761,
|
|
1475
|
+
version: "0.6",
|
|
1476
|
+
name: "new moon"
|
|
1477
|
+
},
|
|
1478
|
+
{
|
|
1479
|
+
start: 127763,
|
|
1480
|
+
end: 127765,
|
|
1481
|
+
version: "0.6",
|
|
1482
|
+
name: "first quarter moon..full moon"
|
|
1483
|
+
},
|
|
1484
|
+
{
|
|
1485
|
+
start: 127769,
|
|
1486
|
+
end: 127769,
|
|
1487
|
+
version: "0.6",
|
|
1488
|
+
name: "crescent moon"
|
|
1489
|
+
},
|
|
1490
|
+
{
|
|
1491
|
+
start: 127771,
|
|
1492
|
+
end: 127771,
|
|
1493
|
+
version: "0.6",
|
|
1494
|
+
name: "first quarter moon face"
|
|
1495
|
+
},
|
|
1496
|
+
{
|
|
1497
|
+
start: 127775,
|
|
1498
|
+
end: 127776,
|
|
1499
|
+
version: "0.6",
|
|
1500
|
+
name: "glowing star..shooting star"
|
|
1501
|
+
},
|
|
1502
|
+
{
|
|
1503
|
+
start: 127792,
|
|
1504
|
+
end: 127793,
|
|
1505
|
+
version: "0.6",
|
|
1506
|
+
name: "chestnut..seedling"
|
|
1507
|
+
},
|
|
1508
|
+
{
|
|
1509
|
+
start: 127796,
|
|
1510
|
+
end: 127797,
|
|
1511
|
+
version: "0.6",
|
|
1512
|
+
name: "palm tree..cactus"
|
|
1513
|
+
},
|
|
1514
|
+
{
|
|
1515
|
+
start: 127799,
|
|
1516
|
+
end: 127818,
|
|
1517
|
+
version: "0.6",
|
|
1518
|
+
name: "tulip..tangerine"
|
|
1519
|
+
},
|
|
1520
|
+
{
|
|
1521
|
+
start: 127820,
|
|
1522
|
+
end: 127823,
|
|
1523
|
+
version: "0.6",
|
|
1524
|
+
name: "banana..green apple"
|
|
1525
|
+
},
|
|
1526
|
+
{
|
|
1527
|
+
start: 127825,
|
|
1528
|
+
end: 127867,
|
|
1529
|
+
version: "0.6",
|
|
1530
|
+
name: "peach..clinking beer mugs"
|
|
1531
|
+
},
|
|
1532
|
+
{
|
|
1533
|
+
start: 127872,
|
|
1534
|
+
end: 127891,
|
|
1535
|
+
version: "0.6",
|
|
1536
|
+
name: "ribbon..graduation cap"
|
|
1537
|
+
},
|
|
1538
|
+
{
|
|
1539
|
+
start: 127904,
|
|
1540
|
+
end: 127940,
|
|
1541
|
+
version: "0.6",
|
|
1542
|
+
name: "carousel horse..person surfing"
|
|
1543
|
+
},
|
|
1544
|
+
{
|
|
1545
|
+
start: 127942,
|
|
1546
|
+
end: 127942,
|
|
1547
|
+
version: "0.6",
|
|
1548
|
+
name: "trophy"
|
|
1549
|
+
},
|
|
1550
|
+
{
|
|
1551
|
+
start: 127944,
|
|
1552
|
+
end: 127944,
|
|
1553
|
+
version: "0.6",
|
|
1554
|
+
name: "american football"
|
|
1555
|
+
},
|
|
1556
|
+
{
|
|
1557
|
+
start: 127946,
|
|
1558
|
+
end: 127946,
|
|
1559
|
+
version: "0.6",
|
|
1560
|
+
name: "person swimming"
|
|
1561
|
+
},
|
|
1562
|
+
{
|
|
1563
|
+
start: 127968,
|
|
1564
|
+
end: 127971,
|
|
1565
|
+
version: "0.6",
|
|
1566
|
+
name: "house..Japanese post office"
|
|
1567
|
+
},
|
|
1568
|
+
{
|
|
1569
|
+
start: 127973,
|
|
1570
|
+
end: 127984,
|
|
1571
|
+
version: "0.6",
|
|
1572
|
+
name: "hospital..castle"
|
|
1573
|
+
},
|
|
1574
|
+
{
|
|
1575
|
+
start: 128012,
|
|
1576
|
+
end: 128014,
|
|
1577
|
+
version: "0.6",
|
|
1578
|
+
name: "snail..horse"
|
|
1579
|
+
},
|
|
1580
|
+
{
|
|
1581
|
+
start: 128017,
|
|
1582
|
+
end: 128018,
|
|
1583
|
+
version: "0.6",
|
|
1584
|
+
name: "ewe..monkey"
|
|
1585
|
+
},
|
|
1586
|
+
{
|
|
1587
|
+
start: 128020,
|
|
1588
|
+
end: 128020,
|
|
1589
|
+
version: "0.6",
|
|
1590
|
+
name: "chicken"
|
|
1591
|
+
},
|
|
1592
|
+
{
|
|
1593
|
+
start: 128023,
|
|
1594
|
+
end: 128041,
|
|
1595
|
+
version: "0.6",
|
|
1596
|
+
name: "boar..poodle"
|
|
1597
|
+
},
|
|
1598
|
+
{
|
|
1599
|
+
start: 128043,
|
|
1600
|
+
end: 128062,
|
|
1601
|
+
version: "0.6",
|
|
1602
|
+
name: "two-hump camel..paw prints"
|
|
1603
|
+
},
|
|
1604
|
+
{
|
|
1605
|
+
start: 128064,
|
|
1606
|
+
end: 128064,
|
|
1607
|
+
version: "0.6",
|
|
1608
|
+
name: "eyes"
|
|
1609
|
+
},
|
|
1610
|
+
{
|
|
1611
|
+
start: 128066,
|
|
1612
|
+
end: 128100,
|
|
1613
|
+
version: "0.6",
|
|
1614
|
+
name: "ear..bust in silhouette"
|
|
1615
|
+
},
|
|
1616
|
+
{
|
|
1617
|
+
start: 128102,
|
|
1618
|
+
end: 128107,
|
|
1619
|
+
version: "0.6",
|
|
1620
|
+
name: "boy..woman and man holding hands"
|
|
1621
|
+
},
|
|
1622
|
+
{
|
|
1623
|
+
start: 128110,
|
|
1624
|
+
end: 128172,
|
|
1625
|
+
version: "0.6",
|
|
1626
|
+
name: "police officer..speech balloon"
|
|
1627
|
+
},
|
|
1628
|
+
{
|
|
1629
|
+
start: 128174,
|
|
1630
|
+
end: 128181,
|
|
1631
|
+
version: "0.6",
|
|
1632
|
+
name: "white flower..dollar banknote"
|
|
1633
|
+
},
|
|
1634
|
+
{
|
|
1635
|
+
start: 128184,
|
|
1636
|
+
end: 128235,
|
|
1637
|
+
version: "0.6",
|
|
1638
|
+
name: "money with wings..closed mailbox with raised flag"
|
|
1639
|
+
},
|
|
1640
|
+
{
|
|
1641
|
+
start: 128238,
|
|
1642
|
+
end: 128238,
|
|
1643
|
+
version: "0.6",
|
|
1644
|
+
name: "postbox"
|
|
1645
|
+
},
|
|
1646
|
+
{
|
|
1647
|
+
start: 128240,
|
|
1648
|
+
end: 128244,
|
|
1649
|
+
version: "0.6",
|
|
1650
|
+
name: "newspaper..mobile phone off"
|
|
1651
|
+
},
|
|
1652
|
+
{
|
|
1653
|
+
start: 128246,
|
|
1654
|
+
end: 128247,
|
|
1655
|
+
version: "0.6",
|
|
1656
|
+
name: "antenna bars..camera"
|
|
1657
|
+
},
|
|
1658
|
+
{
|
|
1659
|
+
start: 128249,
|
|
1660
|
+
end: 128252,
|
|
1661
|
+
version: "0.6",
|
|
1662
|
+
name: "video camera..videocassette"
|
|
1663
|
+
},
|
|
1664
|
+
{
|
|
1665
|
+
start: 128259,
|
|
1666
|
+
end: 128259,
|
|
1667
|
+
version: "0.6",
|
|
1668
|
+
name: "clockwise vertical arrows"
|
|
1669
|
+
},
|
|
1670
|
+
{
|
|
1671
|
+
start: 128266,
|
|
1672
|
+
end: 128276,
|
|
1673
|
+
version: "0.6",
|
|
1674
|
+
name: "speaker high volume..bell"
|
|
1675
|
+
},
|
|
1676
|
+
{
|
|
1677
|
+
start: 128278,
|
|
1678
|
+
end: 128299,
|
|
1679
|
+
version: "0.6",
|
|
1680
|
+
name: "bookmark..water pistol"
|
|
1681
|
+
},
|
|
1682
|
+
{
|
|
1683
|
+
start: 128302,
|
|
1684
|
+
end: 128317,
|
|
1685
|
+
version: "0.6",
|
|
1686
|
+
name: "crystal ball..downwards button"
|
|
1687
|
+
},
|
|
1688
|
+
{
|
|
1689
|
+
start: 128336,
|
|
1690
|
+
end: 128347,
|
|
1691
|
+
version: "0.6",
|
|
1692
|
+
name: "one o’clock..twelve o’clock"
|
|
1693
|
+
},
|
|
1694
|
+
{
|
|
1695
|
+
start: 128507,
|
|
1696
|
+
end: 128511,
|
|
1697
|
+
version: "0.6",
|
|
1698
|
+
name: "mount fuji..moai"
|
|
1699
|
+
},
|
|
1700
|
+
{
|
|
1701
|
+
start: 128513,
|
|
1702
|
+
end: 128518,
|
|
1703
|
+
version: "0.6",
|
|
1704
|
+
name: "beaming face with smiling eyes..grinning squinting face"
|
|
1705
|
+
},
|
|
1706
|
+
{
|
|
1707
|
+
start: 128521,
|
|
1708
|
+
end: 128525,
|
|
1709
|
+
version: "0.6",
|
|
1710
|
+
name: "winking face..smiling face with heart-eyes"
|
|
1711
|
+
},
|
|
1712
|
+
{
|
|
1713
|
+
start: 128527,
|
|
1714
|
+
end: 128527,
|
|
1715
|
+
version: "0.6",
|
|
1716
|
+
name: "smirking face"
|
|
1717
|
+
},
|
|
1718
|
+
{
|
|
1719
|
+
start: 128530,
|
|
1720
|
+
end: 128532,
|
|
1721
|
+
version: "0.6",
|
|
1722
|
+
name: "unamused face..pensive face"
|
|
1723
|
+
},
|
|
1724
|
+
{
|
|
1725
|
+
start: 128534,
|
|
1726
|
+
end: 128534,
|
|
1727
|
+
version: "0.6",
|
|
1728
|
+
name: "confounded face"
|
|
1729
|
+
},
|
|
1730
|
+
{
|
|
1731
|
+
start: 128536,
|
|
1732
|
+
end: 128536,
|
|
1733
|
+
version: "0.6",
|
|
1734
|
+
name: "face blowing a kiss"
|
|
1735
|
+
},
|
|
1736
|
+
{
|
|
1737
|
+
start: 128538,
|
|
1738
|
+
end: 128538,
|
|
1739
|
+
version: "0.6",
|
|
1740
|
+
name: "kissing face with closed eyes"
|
|
1741
|
+
},
|
|
1742
|
+
{
|
|
1743
|
+
start: 128540,
|
|
1744
|
+
end: 128542,
|
|
1745
|
+
version: "0.6",
|
|
1746
|
+
name: "winking face with tongue..disappointed face"
|
|
1747
|
+
},
|
|
1748
|
+
{
|
|
1749
|
+
start: 128544,
|
|
1750
|
+
end: 128549,
|
|
1751
|
+
version: "0.6",
|
|
1752
|
+
name: "angry face..sad but relieved face"
|
|
1753
|
+
},
|
|
1754
|
+
{
|
|
1755
|
+
start: 128552,
|
|
1756
|
+
end: 128555,
|
|
1757
|
+
version: "0.6",
|
|
1758
|
+
name: "fearful face..tired face"
|
|
1759
|
+
},
|
|
1760
|
+
{
|
|
1761
|
+
start: 128557,
|
|
1762
|
+
end: 128557,
|
|
1763
|
+
version: "0.6",
|
|
1764
|
+
name: "loudly crying face"
|
|
1765
|
+
},
|
|
1766
|
+
{
|
|
1767
|
+
start: 128560,
|
|
1768
|
+
end: 128563,
|
|
1769
|
+
version: "0.6",
|
|
1770
|
+
name: "anxious face with sweat..flushed face"
|
|
1771
|
+
},
|
|
1772
|
+
{
|
|
1773
|
+
start: 128565,
|
|
1774
|
+
end: 128565,
|
|
1775
|
+
version: "0.6",
|
|
1776
|
+
name: "face with crossed-out eyes"
|
|
1777
|
+
},
|
|
1778
|
+
{
|
|
1779
|
+
start: 128567,
|
|
1780
|
+
end: 128576,
|
|
1781
|
+
version: "0.6",
|
|
1782
|
+
name: "face with medical mask..weary cat"
|
|
1783
|
+
},
|
|
1784
|
+
{
|
|
1785
|
+
start: 128581,
|
|
1786
|
+
end: 128591,
|
|
1787
|
+
version: "0.6",
|
|
1788
|
+
name: "person gesturing NO..folded hands"
|
|
1789
|
+
},
|
|
1790
|
+
{
|
|
1791
|
+
start: 128640,
|
|
1792
|
+
end: 128640,
|
|
1793
|
+
version: "0.6",
|
|
1794
|
+
name: "rocket"
|
|
1795
|
+
},
|
|
1796
|
+
{
|
|
1797
|
+
start: 128643,
|
|
1798
|
+
end: 128645,
|
|
1799
|
+
version: "0.6",
|
|
1800
|
+
name: "railway car..bullet train"
|
|
1801
|
+
},
|
|
1802
|
+
{
|
|
1803
|
+
start: 128647,
|
|
1804
|
+
end: 128647,
|
|
1805
|
+
version: "0.6",
|
|
1806
|
+
name: "metro"
|
|
1807
|
+
},
|
|
1808
|
+
{
|
|
1809
|
+
start: 128649,
|
|
1810
|
+
end: 128649,
|
|
1811
|
+
version: "0.6",
|
|
1812
|
+
name: "station"
|
|
1813
|
+
},
|
|
1814
|
+
{
|
|
1815
|
+
start: 128652,
|
|
1816
|
+
end: 128652,
|
|
1817
|
+
version: "0.6",
|
|
1818
|
+
name: "bus"
|
|
1819
|
+
},
|
|
1820
|
+
{
|
|
1821
|
+
start: 128655,
|
|
1822
|
+
end: 128655,
|
|
1823
|
+
version: "0.6",
|
|
1824
|
+
name: "bus stop"
|
|
1825
|
+
},
|
|
1826
|
+
{
|
|
1827
|
+
start: 128657,
|
|
1828
|
+
end: 128659,
|
|
1829
|
+
version: "0.6",
|
|
1830
|
+
name: "ambulance..police car"
|
|
1831
|
+
},
|
|
1832
|
+
{
|
|
1833
|
+
start: 128661,
|
|
1834
|
+
end: 128661,
|
|
1835
|
+
version: "0.6",
|
|
1836
|
+
name: "taxi"
|
|
1837
|
+
},
|
|
1838
|
+
{
|
|
1839
|
+
start: 128663,
|
|
1840
|
+
end: 128663,
|
|
1841
|
+
version: "0.6",
|
|
1842
|
+
name: "automobile"
|
|
1843
|
+
},
|
|
1844
|
+
{
|
|
1845
|
+
start: 128665,
|
|
1846
|
+
end: 128666,
|
|
1847
|
+
version: "0.6",
|
|
1848
|
+
name: "sport utility vehicle..delivery truck"
|
|
1849
|
+
},
|
|
1850
|
+
{
|
|
1851
|
+
start: 128674,
|
|
1852
|
+
end: 128674,
|
|
1853
|
+
version: "0.6",
|
|
1854
|
+
name: "ship"
|
|
1855
|
+
},
|
|
1856
|
+
{
|
|
1857
|
+
start: 128676,
|
|
1858
|
+
end: 128677,
|
|
1859
|
+
version: "0.6",
|
|
1860
|
+
name: "speedboat..horizontal traffic light"
|
|
1861
|
+
},
|
|
1862
|
+
{
|
|
1863
|
+
start: 128679,
|
|
1864
|
+
end: 128685,
|
|
1865
|
+
version: "0.6",
|
|
1866
|
+
name: "construction..no smoking"
|
|
1867
|
+
},
|
|
1868
|
+
{
|
|
1869
|
+
start: 128690,
|
|
1870
|
+
end: 128690,
|
|
1871
|
+
version: "0.6",
|
|
1872
|
+
name: "bicycle"
|
|
1873
|
+
},
|
|
1874
|
+
{
|
|
1875
|
+
start: 128694,
|
|
1876
|
+
end: 128694,
|
|
1877
|
+
version: "0.6",
|
|
1878
|
+
name: "person walking"
|
|
1879
|
+
},
|
|
1880
|
+
{
|
|
1881
|
+
start: 128697,
|
|
1882
|
+
end: 128702,
|
|
1883
|
+
version: "0.6",
|
|
1884
|
+
name: "men’s room..water closet"
|
|
1885
|
+
},
|
|
1886
|
+
{
|
|
1887
|
+
start: 128704,
|
|
1888
|
+
end: 128704,
|
|
1889
|
+
version: "0.6",
|
|
1890
|
+
name: "person taking bath"
|
|
1891
|
+
},
|
|
1892
|
+
{
|
|
1893
|
+
start: 9197,
|
|
1894
|
+
end: 9198,
|
|
1895
|
+
version: "0.7",
|
|
1896
|
+
name: "next track button..last track button"
|
|
1897
|
+
},
|
|
1898
|
+
{
|
|
1899
|
+
start: 9208,
|
|
1900
|
+
end: 9210,
|
|
1901
|
+
version: "0.7",
|
|
1902
|
+
name: "pause button..record button"
|
|
1903
|
+
},
|
|
1904
|
+
{
|
|
1905
|
+
start: 9730,
|
|
1906
|
+
end: 9731,
|
|
1907
|
+
version: "0.7",
|
|
1908
|
+
name: "umbrella..snowman"
|
|
1909
|
+
},
|
|
1910
|
+
{
|
|
1911
|
+
start: 9770,
|
|
1912
|
+
end: 9770,
|
|
1913
|
+
version: "0.7",
|
|
1914
|
+
name: "star and crescent"
|
|
1915
|
+
},
|
|
1916
|
+
{
|
|
1917
|
+
start: 9775,
|
|
1918
|
+
end: 9775,
|
|
1919
|
+
version: "0.7",
|
|
1920
|
+
name: "yin yang"
|
|
1921
|
+
},
|
|
1922
|
+
{
|
|
1923
|
+
start: 9784,
|
|
1924
|
+
end: 9785,
|
|
1925
|
+
version: "0.7",
|
|
1926
|
+
name: "wheel of dharma..frowning face"
|
|
1927
|
+
},
|
|
1928
|
+
{
|
|
1929
|
+
start: 9928,
|
|
1930
|
+
end: 9928,
|
|
1931
|
+
version: "0.7",
|
|
1932
|
+
name: "cloud with lightning and rain"
|
|
1933
|
+
},
|
|
1934
|
+
{
|
|
1935
|
+
start: 9935,
|
|
1936
|
+
end: 9935,
|
|
1937
|
+
version: "0.7",
|
|
1938
|
+
name: "pick"
|
|
1939
|
+
},
|
|
1940
|
+
{
|
|
1941
|
+
start: 9937,
|
|
1942
|
+
end: 9937,
|
|
1943
|
+
version: "0.7",
|
|
1944
|
+
name: "rescue worker’s helmet"
|
|
1945
|
+
},
|
|
1946
|
+
{
|
|
1947
|
+
start: 9939,
|
|
1948
|
+
end: 9939,
|
|
1949
|
+
version: "0.7",
|
|
1950
|
+
name: "chains"
|
|
1951
|
+
},
|
|
1952
|
+
{
|
|
1953
|
+
start: 9961,
|
|
1954
|
+
end: 9961,
|
|
1955
|
+
version: "0.7",
|
|
1956
|
+
name: "shinto shrine"
|
|
1957
|
+
},
|
|
1958
|
+
{
|
|
1959
|
+
start: 9968,
|
|
1960
|
+
end: 9969,
|
|
1961
|
+
version: "0.7",
|
|
1962
|
+
name: "mountain..umbrella on ground"
|
|
1963
|
+
},
|
|
1964
|
+
{
|
|
1965
|
+
start: 9972,
|
|
1966
|
+
end: 9972,
|
|
1967
|
+
version: "0.7",
|
|
1968
|
+
name: "ferry"
|
|
1969
|
+
},
|
|
1970
|
+
{
|
|
1971
|
+
start: 9975,
|
|
1972
|
+
end: 9977,
|
|
1973
|
+
version: "0.7",
|
|
1974
|
+
name: "skier..person bouncing ball"
|
|
1975
|
+
},
|
|
1976
|
+
{
|
|
1977
|
+
start: 9997,
|
|
1978
|
+
end: 9997,
|
|
1979
|
+
version: "0.7",
|
|
1980
|
+
name: "writing hand"
|
|
1981
|
+
},
|
|
1982
|
+
{
|
|
1983
|
+
start: 10013,
|
|
1984
|
+
end: 10013,
|
|
1985
|
+
version: "0.7",
|
|
1986
|
+
name: "latin cross"
|
|
1987
|
+
},
|
|
1988
|
+
{
|
|
1989
|
+
start: 10017,
|
|
1990
|
+
end: 10017,
|
|
1991
|
+
version: "0.7",
|
|
1992
|
+
name: "star of David"
|
|
1993
|
+
},
|
|
1994
|
+
{
|
|
1995
|
+
start: 127757,
|
|
1996
|
+
end: 127758,
|
|
1997
|
+
version: "0.7",
|
|
1998
|
+
name: "globe showing Europe-Africa..globe showing Americas"
|
|
1999
|
+
},
|
|
2000
|
+
{
|
|
2001
|
+
start: 127772,
|
|
2002
|
+
end: 127772,
|
|
2003
|
+
version: "0.7",
|
|
2004
|
+
name: "last quarter moon face"
|
|
2005
|
+
},
|
|
2006
|
+
{
|
|
2007
|
+
start: 127777,
|
|
2008
|
+
end: 127777,
|
|
2009
|
+
version: "0.7",
|
|
2010
|
+
name: "thermometer"
|
|
2011
|
+
},
|
|
2012
|
+
{
|
|
2013
|
+
start: 127780,
|
|
2014
|
+
end: 127788,
|
|
2015
|
+
version: "0.7",
|
|
2016
|
+
name: "sun behind small cloud..wind face"
|
|
2017
|
+
},
|
|
2018
|
+
{
|
|
2019
|
+
start: 127798,
|
|
2020
|
+
end: 127798,
|
|
2021
|
+
version: "0.7",
|
|
2022
|
+
name: "hot pepper"
|
|
2023
|
+
},
|
|
2024
|
+
{
|
|
2025
|
+
start: 127869,
|
|
2026
|
+
end: 127869,
|
|
2027
|
+
version: "0.7",
|
|
2028
|
+
name: "fork and knife with plate"
|
|
2029
|
+
},
|
|
2030
|
+
{
|
|
2031
|
+
start: 127894,
|
|
2032
|
+
end: 127895,
|
|
2033
|
+
version: "0.7",
|
|
2034
|
+
name: "military medal..reminder ribbon"
|
|
2035
|
+
},
|
|
2036
|
+
{
|
|
2037
|
+
start: 127897,
|
|
2038
|
+
end: 127899,
|
|
2039
|
+
version: "0.7",
|
|
2040
|
+
name: "studio microphone..control knobs"
|
|
2041
|
+
},
|
|
2042
|
+
{
|
|
2043
|
+
start: 127902,
|
|
2044
|
+
end: 127903,
|
|
2045
|
+
version: "0.7",
|
|
2046
|
+
name: "film frames..admission tickets"
|
|
2047
|
+
},
|
|
2048
|
+
{
|
|
2049
|
+
start: 127947,
|
|
2050
|
+
end: 127950,
|
|
2051
|
+
version: "0.7",
|
|
2052
|
+
name: "person lifting weights..racing car"
|
|
2053
|
+
},
|
|
2054
|
+
{
|
|
2055
|
+
start: 127956,
|
|
2056
|
+
end: 127967,
|
|
2057
|
+
version: "0.7",
|
|
2058
|
+
name: "snow-capped mountain..stadium"
|
|
2059
|
+
},
|
|
2060
|
+
{
|
|
2061
|
+
start: 127987,
|
|
2062
|
+
end: 127987,
|
|
2063
|
+
version: "0.7",
|
|
2064
|
+
name: "white flag"
|
|
2065
|
+
},
|
|
2066
|
+
{
|
|
2067
|
+
start: 127989,
|
|
2068
|
+
end: 127989,
|
|
2069
|
+
version: "0.7",
|
|
2070
|
+
name: "rosette"
|
|
2071
|
+
},
|
|
2072
|
+
{
|
|
2073
|
+
start: 127991,
|
|
2074
|
+
end: 127991,
|
|
2075
|
+
version: "0.7",
|
|
2076
|
+
name: "label"
|
|
2077
|
+
},
|
|
2078
|
+
{
|
|
2079
|
+
start: 128008,
|
|
2080
|
+
end: 128008,
|
|
2081
|
+
version: "0.7",
|
|
2082
|
+
name: "cat"
|
|
2083
|
+
},
|
|
2084
|
+
{
|
|
2085
|
+
start: 128021,
|
|
2086
|
+
end: 128021,
|
|
2087
|
+
version: "0.7",
|
|
2088
|
+
name: "dog"
|
|
2089
|
+
},
|
|
2090
|
+
{
|
|
2091
|
+
start: 128063,
|
|
2092
|
+
end: 128063,
|
|
2093
|
+
version: "0.7",
|
|
2094
|
+
name: "chipmunk"
|
|
2095
|
+
},
|
|
2096
|
+
{
|
|
2097
|
+
start: 128065,
|
|
2098
|
+
end: 128065,
|
|
2099
|
+
version: "0.7",
|
|
2100
|
+
name: "eye"
|
|
2101
|
+
},
|
|
2102
|
+
{
|
|
2103
|
+
start: 128236,
|
|
2104
|
+
end: 128237,
|
|
2105
|
+
version: "0.7",
|
|
2106
|
+
name: "open mailbox with raised flag..open mailbox with lowered flag"
|
|
2107
|
+
},
|
|
2108
|
+
{
|
|
2109
|
+
start: 128253,
|
|
2110
|
+
end: 128253,
|
|
2111
|
+
version: "0.7",
|
|
2112
|
+
name: "film projector"
|
|
2113
|
+
},
|
|
2114
|
+
{
|
|
2115
|
+
start: 128264,
|
|
2116
|
+
end: 128264,
|
|
2117
|
+
version: "0.7",
|
|
2118
|
+
name: "speaker low volume"
|
|
2119
|
+
},
|
|
2120
|
+
{
|
|
2121
|
+
start: 128329,
|
|
2122
|
+
end: 128330,
|
|
2123
|
+
version: "0.7",
|
|
2124
|
+
name: "om..dove"
|
|
2125
|
+
},
|
|
2126
|
+
{
|
|
2127
|
+
start: 128348,
|
|
2128
|
+
end: 128359,
|
|
2129
|
+
version: "0.7",
|
|
2130
|
+
name: "one-thirty..twelve-thirty"
|
|
2131
|
+
},
|
|
2132
|
+
{
|
|
2133
|
+
start: 128367,
|
|
2134
|
+
end: 128368,
|
|
2135
|
+
version: "0.7",
|
|
2136
|
+
name: "candle..mantelpiece clock"
|
|
2137
|
+
},
|
|
2138
|
+
{
|
|
2139
|
+
start: 128371,
|
|
2140
|
+
end: 128377,
|
|
2141
|
+
version: "0.7",
|
|
2142
|
+
name: "hole..joystick"
|
|
2143
|
+
},
|
|
2144
|
+
{
|
|
2145
|
+
start: 128391,
|
|
2146
|
+
end: 128391,
|
|
2147
|
+
version: "0.7",
|
|
2148
|
+
name: "linked paperclips"
|
|
2149
|
+
},
|
|
2150
|
+
{
|
|
2151
|
+
start: 128394,
|
|
2152
|
+
end: 128397,
|
|
2153
|
+
version: "0.7",
|
|
2154
|
+
name: "pen..crayon"
|
|
2155
|
+
},
|
|
2156
|
+
{
|
|
2157
|
+
start: 128400,
|
|
2158
|
+
end: 128400,
|
|
2159
|
+
version: "0.7",
|
|
2160
|
+
name: "hand with fingers splayed"
|
|
2161
|
+
},
|
|
2162
|
+
{
|
|
2163
|
+
start: 128421,
|
|
2164
|
+
end: 128421,
|
|
2165
|
+
version: "0.7",
|
|
2166
|
+
name: "desktop computer"
|
|
2167
|
+
},
|
|
2168
|
+
{
|
|
2169
|
+
start: 128424,
|
|
2170
|
+
end: 128424,
|
|
2171
|
+
version: "0.7",
|
|
2172
|
+
name: "printer"
|
|
2173
|
+
},
|
|
2174
|
+
{
|
|
2175
|
+
start: 128433,
|
|
2176
|
+
end: 128434,
|
|
2177
|
+
version: "0.7",
|
|
2178
|
+
name: "computer mouse..trackball"
|
|
2179
|
+
},
|
|
2180
|
+
{
|
|
2181
|
+
start: 128444,
|
|
2182
|
+
end: 128444,
|
|
2183
|
+
version: "0.7",
|
|
2184
|
+
name: "framed picture"
|
|
2185
|
+
},
|
|
2186
|
+
{
|
|
2187
|
+
start: 128450,
|
|
2188
|
+
end: 128452,
|
|
2189
|
+
version: "0.7",
|
|
2190
|
+
name: "card index dividers..file cabinet"
|
|
2191
|
+
},
|
|
2192
|
+
{
|
|
2193
|
+
start: 128465,
|
|
2194
|
+
end: 128467,
|
|
2195
|
+
version: "0.7",
|
|
2196
|
+
name: "wastebasket..spiral calendar"
|
|
2197
|
+
},
|
|
2198
|
+
{
|
|
2199
|
+
start: 128476,
|
|
2200
|
+
end: 128478,
|
|
2201
|
+
version: "0.7",
|
|
2202
|
+
name: "clamp..rolled-up newspaper"
|
|
2203
|
+
},
|
|
2204
|
+
{
|
|
2205
|
+
start: 128481,
|
|
2206
|
+
end: 128481,
|
|
2207
|
+
version: "0.7",
|
|
2208
|
+
name: "dagger"
|
|
2209
|
+
},
|
|
2210
|
+
{
|
|
2211
|
+
start: 128483,
|
|
2212
|
+
end: 128483,
|
|
2213
|
+
version: "0.7",
|
|
2214
|
+
name: "speaking head"
|
|
2215
|
+
},
|
|
2216
|
+
{
|
|
2217
|
+
start: 128495,
|
|
2218
|
+
end: 128495,
|
|
2219
|
+
version: "0.7",
|
|
2220
|
+
name: "right anger bubble"
|
|
2221
|
+
},
|
|
2222
|
+
{
|
|
2223
|
+
start: 128499,
|
|
2224
|
+
end: 128499,
|
|
2225
|
+
version: "0.7",
|
|
2226
|
+
name: "ballot box with ballot"
|
|
2227
|
+
},
|
|
2228
|
+
{
|
|
2229
|
+
start: 128506,
|
|
2230
|
+
end: 128506,
|
|
2231
|
+
version: "0.7",
|
|
2232
|
+
name: "world map"
|
|
2233
|
+
},
|
|
2234
|
+
{
|
|
2235
|
+
start: 128528,
|
|
2236
|
+
end: 128528,
|
|
2237
|
+
version: "0.7",
|
|
2238
|
+
name: "neutral face"
|
|
2239
|
+
},
|
|
2240
|
+
{
|
|
2241
|
+
start: 128653,
|
|
2242
|
+
end: 128653,
|
|
2243
|
+
version: "0.7",
|
|
2244
|
+
name: "oncoming bus"
|
|
2245
|
+
},
|
|
2246
|
+
{
|
|
2247
|
+
start: 128660,
|
|
2248
|
+
end: 128660,
|
|
2249
|
+
version: "0.7",
|
|
2250
|
+
name: "oncoming police car"
|
|
2251
|
+
},
|
|
2252
|
+
{
|
|
2253
|
+
start: 128664,
|
|
2254
|
+
end: 128664,
|
|
2255
|
+
version: "0.7",
|
|
2256
|
+
name: "oncoming automobile"
|
|
2257
|
+
},
|
|
2258
|
+
{
|
|
2259
|
+
start: 128715,
|
|
2260
|
+
end: 128715,
|
|
2261
|
+
version: "0.7",
|
|
2262
|
+
name: "couch and lamp"
|
|
2263
|
+
},
|
|
2264
|
+
{
|
|
2265
|
+
start: 128717,
|
|
2266
|
+
end: 128719,
|
|
2267
|
+
version: "0.7",
|
|
2268
|
+
name: "shopping bags..bed"
|
|
2269
|
+
},
|
|
2270
|
+
{
|
|
2271
|
+
start: 128736,
|
|
2272
|
+
end: 128741,
|
|
2273
|
+
version: "0.7",
|
|
2274
|
+
name: "hammer and wrench..motor boat"
|
|
2275
|
+
},
|
|
2276
|
+
{
|
|
2277
|
+
start: 128745,
|
|
2278
|
+
end: 128745,
|
|
2279
|
+
version: "0.7",
|
|
2280
|
+
name: "small airplane"
|
|
2281
|
+
},
|
|
2282
|
+
{
|
|
2283
|
+
start: 128752,
|
|
2284
|
+
end: 128752,
|
|
2285
|
+
version: "0.7",
|
|
2286
|
+
name: "satellite"
|
|
2287
|
+
},
|
|
2288
|
+
{
|
|
2289
|
+
start: 128755,
|
|
2290
|
+
end: 128755,
|
|
2291
|
+
version: "0.7",
|
|
2292
|
+
name: "passenger ship"
|
|
2293
|
+
},
|
|
2294
|
+
{
|
|
2295
|
+
start: 9e3,
|
|
2296
|
+
end: 9e3,
|
|
2297
|
+
version: "1.0",
|
|
2298
|
+
name: "keyboard"
|
|
2299
|
+
},
|
|
2300
|
+
{
|
|
2301
|
+
start: 9167,
|
|
2302
|
+
end: 9167,
|
|
2303
|
+
version: "1.0",
|
|
2304
|
+
name: "eject button"
|
|
2305
|
+
},
|
|
2306
|
+
{
|
|
2307
|
+
start: 9199,
|
|
2308
|
+
end: 9199,
|
|
2309
|
+
version: "1.0",
|
|
2310
|
+
name: "play or pause button"
|
|
2311
|
+
},
|
|
2312
|
+
{
|
|
2313
|
+
start: 9201,
|
|
2314
|
+
end: 9202,
|
|
2315
|
+
version: "1.0",
|
|
2316
|
+
name: "stopwatch..timer clock"
|
|
2317
|
+
},
|
|
2318
|
+
{
|
|
2319
|
+
start: 9732,
|
|
2320
|
+
end: 9732,
|
|
2321
|
+
version: "1.0",
|
|
2322
|
+
name: "comet"
|
|
2323
|
+
},
|
|
2324
|
+
{
|
|
2325
|
+
start: 9752,
|
|
2326
|
+
end: 9752,
|
|
2327
|
+
version: "1.0",
|
|
2328
|
+
name: "shamrock"
|
|
2329
|
+
},
|
|
2330
|
+
{
|
|
2331
|
+
start: 9760,
|
|
2332
|
+
end: 9760,
|
|
2333
|
+
version: "1.0",
|
|
2334
|
+
name: "skull and crossbones"
|
|
2335
|
+
},
|
|
2336
|
+
{
|
|
2337
|
+
start: 9762,
|
|
2338
|
+
end: 9763,
|
|
2339
|
+
version: "1.0",
|
|
2340
|
+
name: "radioactive..biohazard"
|
|
2341
|
+
},
|
|
2342
|
+
{
|
|
2343
|
+
start: 9766,
|
|
2344
|
+
end: 9766,
|
|
2345
|
+
version: "1.0",
|
|
2346
|
+
name: "orthodox cross"
|
|
2347
|
+
},
|
|
2348
|
+
{
|
|
2349
|
+
start: 9774,
|
|
2350
|
+
end: 9774,
|
|
2351
|
+
version: "1.0",
|
|
2352
|
+
name: "peace symbol"
|
|
2353
|
+
},
|
|
2354
|
+
{
|
|
2355
|
+
start: 9874,
|
|
2356
|
+
end: 9874,
|
|
2357
|
+
version: "1.0",
|
|
2358
|
+
name: "hammer and pick"
|
|
2359
|
+
},
|
|
2360
|
+
{
|
|
2361
|
+
start: 9876,
|
|
2362
|
+
end: 9876,
|
|
2363
|
+
version: "1.0",
|
|
2364
|
+
name: "crossed swords"
|
|
2365
|
+
},
|
|
2366
|
+
{
|
|
2367
|
+
start: 9878,
|
|
2368
|
+
end: 9879,
|
|
2369
|
+
version: "1.0",
|
|
2370
|
+
name: "balance scale..alembic"
|
|
2371
|
+
},
|
|
2372
|
+
{
|
|
2373
|
+
start: 9881,
|
|
2374
|
+
end: 9881,
|
|
2375
|
+
version: "1.0",
|
|
2376
|
+
name: "gear"
|
|
2377
|
+
},
|
|
2378
|
+
{
|
|
2379
|
+
start: 9883,
|
|
2380
|
+
end: 9884,
|
|
2381
|
+
version: "1.0",
|
|
2382
|
+
name: "atom symbol..fleur-de-lis"
|
|
2383
|
+
},
|
|
2384
|
+
{
|
|
2385
|
+
start: 9904,
|
|
2386
|
+
end: 9905,
|
|
2387
|
+
version: "1.0",
|
|
2388
|
+
name: "coffin..funeral urn"
|
|
2389
|
+
},
|
|
2390
|
+
{
|
|
2391
|
+
start: 10083,
|
|
2392
|
+
end: 10083,
|
|
2393
|
+
version: "1.0",
|
|
2394
|
+
name: "heart exclamation"
|
|
2395
|
+
},
|
|
2396
|
+
{
|
|
2397
|
+
start: 10175,
|
|
2398
|
+
end: 10175,
|
|
2399
|
+
version: "1.0",
|
|
2400
|
+
name: "double curly loop"
|
|
2401
|
+
},
|
|
2402
|
+
{
|
|
2403
|
+
start: 127760,
|
|
2404
|
+
end: 127760,
|
|
2405
|
+
version: "1.0",
|
|
2406
|
+
name: "globe with meridians"
|
|
2407
|
+
},
|
|
2408
|
+
{
|
|
2409
|
+
start: 127762,
|
|
2410
|
+
end: 127762,
|
|
2411
|
+
version: "1.0",
|
|
2412
|
+
name: "waxing crescent moon"
|
|
2413
|
+
},
|
|
2414
|
+
{
|
|
2415
|
+
start: 127766,
|
|
2416
|
+
end: 127768,
|
|
2417
|
+
version: "1.0",
|
|
2418
|
+
name: "waning gibbous moon..waning crescent moon"
|
|
2419
|
+
},
|
|
2420
|
+
{
|
|
2421
|
+
start: 127770,
|
|
2422
|
+
end: 127770,
|
|
2423
|
+
version: "1.0",
|
|
2424
|
+
name: "new moon face"
|
|
2425
|
+
},
|
|
2426
|
+
{
|
|
2427
|
+
start: 127773,
|
|
2428
|
+
end: 127774,
|
|
2429
|
+
version: "1.0",
|
|
2430
|
+
name: "full moon face..sun with face"
|
|
2431
|
+
},
|
|
2432
|
+
{
|
|
2433
|
+
start: 127789,
|
|
2434
|
+
end: 127791,
|
|
2435
|
+
version: "1.0",
|
|
2436
|
+
name: "hot dog..burrito"
|
|
2437
|
+
},
|
|
2438
|
+
{
|
|
2439
|
+
start: 127794,
|
|
2440
|
+
end: 127795,
|
|
2441
|
+
version: "1.0",
|
|
2442
|
+
name: "evergreen tree..deciduous tree"
|
|
2443
|
+
},
|
|
2444
|
+
{
|
|
2445
|
+
start: 127819,
|
|
2446
|
+
end: 127819,
|
|
2447
|
+
version: "1.0",
|
|
2448
|
+
name: "lemon"
|
|
2449
|
+
},
|
|
2450
|
+
{
|
|
2451
|
+
start: 127824,
|
|
2452
|
+
end: 127824,
|
|
2453
|
+
version: "1.0",
|
|
2454
|
+
name: "pear"
|
|
2455
|
+
},
|
|
2456
|
+
{
|
|
2457
|
+
start: 127868,
|
|
2458
|
+
end: 127868,
|
|
2459
|
+
version: "1.0",
|
|
2460
|
+
name: "baby bottle"
|
|
2461
|
+
},
|
|
2462
|
+
{
|
|
2463
|
+
start: 127870,
|
|
2464
|
+
end: 127871,
|
|
2465
|
+
version: "1.0",
|
|
2466
|
+
name: "bottle with popping cork..popcorn"
|
|
2467
|
+
},
|
|
2468
|
+
{
|
|
2469
|
+
start: 127941,
|
|
2470
|
+
end: 127941,
|
|
2471
|
+
version: "1.0",
|
|
2472
|
+
name: "sports medal"
|
|
2473
|
+
},
|
|
2474
|
+
{
|
|
2475
|
+
start: 127943,
|
|
2476
|
+
end: 127943,
|
|
2477
|
+
version: "1.0",
|
|
2478
|
+
name: "horse racing"
|
|
2479
|
+
},
|
|
2480
|
+
{
|
|
2481
|
+
start: 127945,
|
|
2482
|
+
end: 127945,
|
|
2483
|
+
version: "1.0",
|
|
2484
|
+
name: "rugby football"
|
|
2485
|
+
},
|
|
2486
|
+
{
|
|
2487
|
+
start: 127951,
|
|
2488
|
+
end: 127955,
|
|
2489
|
+
version: "1.0",
|
|
2490
|
+
name: "cricket game..ping pong"
|
|
2491
|
+
},
|
|
2492
|
+
{
|
|
2493
|
+
start: 127972,
|
|
2494
|
+
end: 127972,
|
|
2495
|
+
version: "1.0",
|
|
2496
|
+
name: "post office"
|
|
2497
|
+
},
|
|
2498
|
+
{
|
|
2499
|
+
start: 127988,
|
|
2500
|
+
end: 127988,
|
|
2501
|
+
version: "1.0",
|
|
2502
|
+
name: "black flag"
|
|
2503
|
+
},
|
|
2504
|
+
{
|
|
2505
|
+
start: 127992,
|
|
2506
|
+
end: 128007,
|
|
2507
|
+
version: "1.0",
|
|
2508
|
+
name: "badminton..rabbit"
|
|
2509
|
+
},
|
|
2510
|
+
{
|
|
2511
|
+
start: 128009,
|
|
2512
|
+
end: 128011,
|
|
2513
|
+
version: "1.0",
|
|
2514
|
+
name: "dragon..whale"
|
|
2515
|
+
},
|
|
2516
|
+
{
|
|
2517
|
+
start: 128015,
|
|
2518
|
+
end: 128016,
|
|
2519
|
+
version: "1.0",
|
|
2520
|
+
name: "ram..goat"
|
|
2521
|
+
},
|
|
2522
|
+
{
|
|
2523
|
+
start: 128019,
|
|
2524
|
+
end: 128019,
|
|
2525
|
+
version: "1.0",
|
|
2526
|
+
name: "rooster"
|
|
2527
|
+
},
|
|
2528
|
+
{
|
|
2529
|
+
start: 128022,
|
|
2530
|
+
end: 128022,
|
|
2531
|
+
version: "1.0",
|
|
2532
|
+
name: "pig"
|
|
2533
|
+
},
|
|
2534
|
+
{
|
|
2535
|
+
start: 128042,
|
|
2536
|
+
end: 128042,
|
|
2537
|
+
version: "1.0",
|
|
2538
|
+
name: "camel"
|
|
2539
|
+
},
|
|
2540
|
+
{
|
|
2541
|
+
start: 128101,
|
|
2542
|
+
end: 128101,
|
|
2543
|
+
version: "1.0",
|
|
2544
|
+
name: "busts in silhouette"
|
|
2545
|
+
},
|
|
2546
|
+
{
|
|
2547
|
+
start: 128108,
|
|
2548
|
+
end: 128109,
|
|
2549
|
+
version: "1.0",
|
|
2550
|
+
name: "men holding hands..women holding hands"
|
|
2551
|
+
},
|
|
2552
|
+
{
|
|
2553
|
+
start: 128173,
|
|
2554
|
+
end: 128173,
|
|
2555
|
+
version: "1.0",
|
|
2556
|
+
name: "thought balloon"
|
|
2557
|
+
},
|
|
2558
|
+
{
|
|
2559
|
+
start: 128182,
|
|
2560
|
+
end: 128183,
|
|
2561
|
+
version: "1.0",
|
|
2562
|
+
name: "euro banknote..pound banknote"
|
|
2563
|
+
},
|
|
2564
|
+
{
|
|
2565
|
+
start: 128239,
|
|
2566
|
+
end: 128239,
|
|
2567
|
+
version: "1.0",
|
|
2568
|
+
name: "postal horn"
|
|
2569
|
+
},
|
|
2570
|
+
{
|
|
2571
|
+
start: 128245,
|
|
2572
|
+
end: 128245,
|
|
2573
|
+
version: "1.0",
|
|
2574
|
+
name: "no mobile phones"
|
|
2575
|
+
},
|
|
2576
|
+
{
|
|
2577
|
+
start: 128248,
|
|
2578
|
+
end: 128248,
|
|
2579
|
+
version: "1.0",
|
|
2580
|
+
name: "camera with flash"
|
|
2581
|
+
},
|
|
2582
|
+
{
|
|
2583
|
+
start: 128255,
|
|
2584
|
+
end: 128258,
|
|
2585
|
+
version: "1.0",
|
|
2586
|
+
name: "prayer beads..repeat single button"
|
|
2587
|
+
},
|
|
2588
|
+
{
|
|
2589
|
+
start: 128260,
|
|
2590
|
+
end: 128263,
|
|
2591
|
+
version: "1.0",
|
|
2592
|
+
name: "counterclockwise arrows button..muted speaker"
|
|
2593
|
+
},
|
|
2594
|
+
{
|
|
2595
|
+
start: 128265,
|
|
2596
|
+
end: 128265,
|
|
2597
|
+
version: "1.0",
|
|
2598
|
+
name: "speaker medium volume"
|
|
2599
|
+
},
|
|
2600
|
+
{
|
|
2601
|
+
start: 128277,
|
|
2602
|
+
end: 128277,
|
|
2603
|
+
version: "1.0",
|
|
2604
|
+
name: "bell with slash"
|
|
2605
|
+
},
|
|
2606
|
+
{
|
|
2607
|
+
start: 128300,
|
|
2608
|
+
end: 128301,
|
|
2609
|
+
version: "1.0",
|
|
2610
|
+
name: "microscope..telescope"
|
|
2611
|
+
},
|
|
2612
|
+
{
|
|
2613
|
+
start: 128331,
|
|
2614
|
+
end: 128334,
|
|
2615
|
+
version: "1.0",
|
|
2616
|
+
name: "kaaba..menorah"
|
|
2617
|
+
},
|
|
2618
|
+
{
|
|
2619
|
+
start: 128405,
|
|
2620
|
+
end: 128406,
|
|
2621
|
+
version: "1.0",
|
|
2622
|
+
name: "middle finger..vulcan salute"
|
|
2623
|
+
},
|
|
2624
|
+
{
|
|
2625
|
+
start: 128512,
|
|
2626
|
+
end: 128512,
|
|
2627
|
+
version: "1.0",
|
|
2628
|
+
name: "grinning face"
|
|
2629
|
+
},
|
|
2630
|
+
{
|
|
2631
|
+
start: 128519,
|
|
2632
|
+
end: 128520,
|
|
2633
|
+
version: "1.0",
|
|
2634
|
+
name: "smiling face with halo..smiling face with horns"
|
|
2635
|
+
},
|
|
2636
|
+
{
|
|
2637
|
+
start: 128526,
|
|
2638
|
+
end: 128526,
|
|
2639
|
+
version: "1.0",
|
|
2640
|
+
name: "smiling face with sunglasses"
|
|
2641
|
+
},
|
|
2642
|
+
{
|
|
2643
|
+
start: 128529,
|
|
2644
|
+
end: 128529,
|
|
2645
|
+
version: "1.0",
|
|
2646
|
+
name: "expressionless face"
|
|
2647
|
+
},
|
|
2648
|
+
{
|
|
2649
|
+
start: 128533,
|
|
2650
|
+
end: 128533,
|
|
2651
|
+
version: "1.0",
|
|
2652
|
+
name: "confused face"
|
|
2653
|
+
},
|
|
2654
|
+
{
|
|
2655
|
+
start: 128535,
|
|
2656
|
+
end: 128535,
|
|
2657
|
+
version: "1.0",
|
|
2658
|
+
name: "kissing face"
|
|
2659
|
+
},
|
|
2660
|
+
{
|
|
2661
|
+
start: 128537,
|
|
2662
|
+
end: 128537,
|
|
2663
|
+
version: "1.0",
|
|
2664
|
+
name: "kissing face with smiling eyes"
|
|
2665
|
+
},
|
|
2666
|
+
{
|
|
2667
|
+
start: 128539,
|
|
2668
|
+
end: 128539,
|
|
2669
|
+
version: "1.0",
|
|
2670
|
+
name: "face with tongue"
|
|
2671
|
+
},
|
|
2672
|
+
{
|
|
2673
|
+
start: 128543,
|
|
2674
|
+
end: 128543,
|
|
2675
|
+
version: "1.0",
|
|
2676
|
+
name: "worried face"
|
|
2677
|
+
},
|
|
2678
|
+
{
|
|
2679
|
+
start: 128550,
|
|
2680
|
+
end: 128551,
|
|
2681
|
+
version: "1.0",
|
|
2682
|
+
name: "frowning face with open mouth..anguished face"
|
|
2683
|
+
},
|
|
2684
|
+
{
|
|
2685
|
+
start: 128556,
|
|
2686
|
+
end: 128556,
|
|
2687
|
+
version: "1.0",
|
|
2688
|
+
name: "grimacing face"
|
|
2689
|
+
},
|
|
2690
|
+
{
|
|
2691
|
+
start: 128558,
|
|
2692
|
+
end: 128559,
|
|
2693
|
+
version: "1.0",
|
|
2694
|
+
name: "face with open mouth..hushed face"
|
|
2695
|
+
},
|
|
2696
|
+
{
|
|
2697
|
+
start: 128564,
|
|
2698
|
+
end: 128564,
|
|
2699
|
+
version: "1.0",
|
|
2700
|
+
name: "sleeping face"
|
|
2701
|
+
},
|
|
2702
|
+
{
|
|
2703
|
+
start: 128566,
|
|
2704
|
+
end: 128566,
|
|
2705
|
+
version: "1.0",
|
|
2706
|
+
name: "face without mouth"
|
|
2707
|
+
},
|
|
2708
|
+
{
|
|
2709
|
+
start: 128577,
|
|
2710
|
+
end: 128580,
|
|
2711
|
+
version: "1.0",
|
|
2712
|
+
name: "slightly frowning face..face with rolling eyes"
|
|
2713
|
+
},
|
|
2714
|
+
{
|
|
2715
|
+
start: 128641,
|
|
2716
|
+
end: 128642,
|
|
2717
|
+
version: "1.0",
|
|
2718
|
+
name: "helicopter..locomotive"
|
|
2719
|
+
},
|
|
2720
|
+
{
|
|
2721
|
+
start: 128646,
|
|
2722
|
+
end: 128646,
|
|
2723
|
+
version: "1.0",
|
|
2724
|
+
name: "train"
|
|
2725
|
+
},
|
|
2726
|
+
{
|
|
2727
|
+
start: 128648,
|
|
2728
|
+
end: 128648,
|
|
2729
|
+
version: "1.0",
|
|
2730
|
+
name: "light rail"
|
|
2731
|
+
},
|
|
2732
|
+
{
|
|
2733
|
+
start: 128650,
|
|
2734
|
+
end: 128651,
|
|
2735
|
+
version: "1.0",
|
|
2736
|
+
name: "tram..tram car"
|
|
2737
|
+
},
|
|
2738
|
+
{
|
|
2739
|
+
start: 128654,
|
|
2740
|
+
end: 128654,
|
|
2741
|
+
version: "1.0",
|
|
2742
|
+
name: "trolleybus"
|
|
2743
|
+
},
|
|
2744
|
+
{
|
|
2745
|
+
start: 128656,
|
|
2746
|
+
end: 128656,
|
|
2747
|
+
version: "1.0",
|
|
2748
|
+
name: "minibus"
|
|
2749
|
+
},
|
|
2750
|
+
{
|
|
2751
|
+
start: 128662,
|
|
2752
|
+
end: 128662,
|
|
2753
|
+
version: "1.0",
|
|
2754
|
+
name: "oncoming taxi"
|
|
2755
|
+
},
|
|
2756
|
+
{
|
|
2757
|
+
start: 128667,
|
|
2758
|
+
end: 128673,
|
|
2759
|
+
version: "1.0",
|
|
2760
|
+
name: "articulated lorry..aerial tramway"
|
|
2761
|
+
},
|
|
2762
|
+
{
|
|
2763
|
+
start: 128675,
|
|
2764
|
+
end: 128675,
|
|
2765
|
+
version: "1.0",
|
|
2766
|
+
name: "person rowing boat"
|
|
2767
|
+
},
|
|
2768
|
+
{
|
|
2769
|
+
start: 128678,
|
|
2770
|
+
end: 128678,
|
|
2771
|
+
version: "1.0",
|
|
2772
|
+
name: "vertical traffic light"
|
|
2773
|
+
},
|
|
2774
|
+
{
|
|
2775
|
+
start: 128686,
|
|
2776
|
+
end: 128689,
|
|
2777
|
+
version: "1.0",
|
|
2778
|
+
name: "litter in bin sign..non-potable water"
|
|
2779
|
+
},
|
|
2780
|
+
{
|
|
2781
|
+
start: 128691,
|
|
2782
|
+
end: 128693,
|
|
2783
|
+
version: "1.0",
|
|
2784
|
+
name: "no bicycles..person mountain biking"
|
|
2785
|
+
},
|
|
2786
|
+
{
|
|
2787
|
+
start: 128695,
|
|
2788
|
+
end: 128696,
|
|
2789
|
+
version: "1.0",
|
|
2790
|
+
name: "no pedestrians..children crossing"
|
|
2791
|
+
},
|
|
2792
|
+
{
|
|
2793
|
+
start: 128703,
|
|
2794
|
+
end: 128703,
|
|
2795
|
+
version: "1.0",
|
|
2796
|
+
name: "shower"
|
|
2797
|
+
},
|
|
2798
|
+
{
|
|
2799
|
+
start: 128705,
|
|
2800
|
+
end: 128709,
|
|
2801
|
+
version: "1.0",
|
|
2802
|
+
name: "bathtub..left luggage"
|
|
2803
|
+
},
|
|
2804
|
+
{
|
|
2805
|
+
start: 128716,
|
|
2806
|
+
end: 128716,
|
|
2807
|
+
version: "1.0",
|
|
2808
|
+
name: "person in bed"
|
|
2809
|
+
},
|
|
2810
|
+
{
|
|
2811
|
+
start: 128720,
|
|
2812
|
+
end: 128720,
|
|
2813
|
+
version: "1.0",
|
|
2814
|
+
name: "place of worship"
|
|
2815
|
+
},
|
|
2816
|
+
{
|
|
2817
|
+
start: 128747,
|
|
2818
|
+
end: 128748,
|
|
2819
|
+
version: "1.0",
|
|
2820
|
+
name: "airplane departure..airplane arrival"
|
|
2821
|
+
},
|
|
2822
|
+
{
|
|
2823
|
+
start: 129296,
|
|
2824
|
+
end: 129304,
|
|
2825
|
+
version: "1.0",
|
|
2826
|
+
name: "zipper-mouth face..sign of the horns"
|
|
2827
|
+
},
|
|
2828
|
+
{
|
|
2829
|
+
start: 129408,
|
|
2830
|
+
end: 129412,
|
|
2831
|
+
version: "1.0",
|
|
2832
|
+
name: "crab..unicorn"
|
|
2833
|
+
},
|
|
2834
|
+
{
|
|
2835
|
+
start: 129472,
|
|
2836
|
+
end: 129472,
|
|
2837
|
+
version: "1.0",
|
|
2838
|
+
name: "cheese wedge"
|
|
2839
|
+
},
|
|
2840
|
+
{
|
|
2841
|
+
start: 128488,
|
|
2842
|
+
end: 128488,
|
|
2843
|
+
version: "2.0",
|
|
2844
|
+
name: "left speech bubble"
|
|
2845
|
+
},
|
|
2846
|
+
{
|
|
2847
|
+
start: 128378,
|
|
2848
|
+
end: 128378,
|
|
2849
|
+
version: "3.0",
|
|
2850
|
+
name: "man dancing"
|
|
2851
|
+
},
|
|
2852
|
+
{
|
|
2853
|
+
start: 128420,
|
|
2854
|
+
end: 128420,
|
|
2855
|
+
version: "3.0",
|
|
2856
|
+
name: "black heart"
|
|
2857
|
+
},
|
|
2858
|
+
{
|
|
2859
|
+
start: 128721,
|
|
2860
|
+
end: 128722,
|
|
2861
|
+
version: "3.0",
|
|
2862
|
+
name: "stop sign..shopping cart"
|
|
2863
|
+
},
|
|
2864
|
+
{
|
|
2865
|
+
start: 128756,
|
|
2866
|
+
end: 128758,
|
|
2867
|
+
version: "3.0",
|
|
2868
|
+
name: "kick scooter..canoe"
|
|
2869
|
+
},
|
|
2870
|
+
{
|
|
2871
|
+
start: 129305,
|
|
2872
|
+
end: 129310,
|
|
2873
|
+
version: "3.0",
|
|
2874
|
+
name: "call me hand..crossed fingers"
|
|
2875
|
+
},
|
|
2876
|
+
{
|
|
2877
|
+
start: 129312,
|
|
2878
|
+
end: 129319,
|
|
2879
|
+
version: "3.0",
|
|
2880
|
+
name: "cowboy hat face..sneezing face"
|
|
2881
|
+
},
|
|
2882
|
+
{
|
|
2883
|
+
start: 129328,
|
|
2884
|
+
end: 129328,
|
|
2885
|
+
version: "3.0",
|
|
2886
|
+
name: "pregnant woman"
|
|
2887
|
+
},
|
|
2888
|
+
{
|
|
2889
|
+
start: 129331,
|
|
2890
|
+
end: 129338,
|
|
2891
|
+
version: "3.0",
|
|
2892
|
+
name: "selfie..person fencing"
|
|
2893
|
+
},
|
|
2894
|
+
{
|
|
2895
|
+
start: 129340,
|
|
2896
|
+
end: 129342,
|
|
2897
|
+
version: "3.0",
|
|
2898
|
+
name: "people wrestling..person playing handball"
|
|
2899
|
+
},
|
|
2900
|
+
{
|
|
2901
|
+
start: 129344,
|
|
2902
|
+
end: 129349,
|
|
2903
|
+
version: "3.0",
|
|
2904
|
+
name: "wilted flower..goal net"
|
|
2905
|
+
},
|
|
2906
|
+
{
|
|
2907
|
+
start: 129351,
|
|
2908
|
+
end: 129355,
|
|
2909
|
+
version: "3.0",
|
|
2910
|
+
name: "1st place medal..martial arts uniform"
|
|
2911
|
+
},
|
|
2912
|
+
{
|
|
2913
|
+
start: 129360,
|
|
2914
|
+
end: 129374,
|
|
2915
|
+
version: "3.0",
|
|
2916
|
+
name: "croissant..pancakes"
|
|
2917
|
+
},
|
|
2918
|
+
{
|
|
2919
|
+
start: 129413,
|
|
2920
|
+
end: 129425,
|
|
2921
|
+
version: "3.0",
|
|
2922
|
+
name: "eagle..squid"
|
|
2923
|
+
},
|
|
2924
|
+
{
|
|
2925
|
+
start: 9792,
|
|
2926
|
+
end: 9792,
|
|
2927
|
+
version: "4.0",
|
|
2928
|
+
name: "female sign"
|
|
2929
|
+
},
|
|
2930
|
+
{
|
|
2931
|
+
start: 9794,
|
|
2932
|
+
end: 9794,
|
|
2933
|
+
version: "4.0",
|
|
2934
|
+
name: "male sign"
|
|
2935
|
+
},
|
|
2936
|
+
{
|
|
2937
|
+
start: 9877,
|
|
2938
|
+
end: 9877,
|
|
2939
|
+
version: "4.0",
|
|
2940
|
+
name: "medical symbol"
|
|
2941
|
+
},
|
|
2942
|
+
{
|
|
2943
|
+
start: 128759,
|
|
2944
|
+
end: 128760,
|
|
2945
|
+
version: "5.0",
|
|
2946
|
+
name: "sled..flying saucer"
|
|
2947
|
+
},
|
|
2948
|
+
{
|
|
2949
|
+
start: 129311,
|
|
2950
|
+
end: 129311,
|
|
2951
|
+
version: "5.0",
|
|
2952
|
+
name: "love-you gesture"
|
|
2953
|
+
},
|
|
2954
|
+
{
|
|
2955
|
+
start: 129320,
|
|
2956
|
+
end: 129327,
|
|
2957
|
+
version: "5.0",
|
|
2958
|
+
name: "face with raised eyebrow..exploding head"
|
|
2959
|
+
},
|
|
2960
|
+
{
|
|
2961
|
+
start: 129329,
|
|
2962
|
+
end: 129330,
|
|
2963
|
+
version: "5.0",
|
|
2964
|
+
name: "breast-feeding..palms up together"
|
|
2965
|
+
},
|
|
2966
|
+
{
|
|
2967
|
+
start: 129356,
|
|
2968
|
+
end: 129356,
|
|
2969
|
+
version: "5.0",
|
|
2970
|
+
name: "curling stone"
|
|
2971
|
+
},
|
|
2972
|
+
{
|
|
2973
|
+
start: 129375,
|
|
2974
|
+
end: 129387,
|
|
2975
|
+
version: "5.0",
|
|
2976
|
+
name: "dumpling..canned food"
|
|
2977
|
+
},
|
|
2978
|
+
{
|
|
2979
|
+
start: 129426,
|
|
2980
|
+
end: 129431,
|
|
2981
|
+
version: "5.0",
|
|
2982
|
+
name: "giraffe..cricket"
|
|
2983
|
+
},
|
|
2984
|
+
{
|
|
2985
|
+
start: 129488,
|
|
2986
|
+
end: 129510,
|
|
2987
|
+
version: "5.0",
|
|
2988
|
+
name: "face with monocle..socks"
|
|
2989
|
+
},
|
|
2990
|
+
{
|
|
2991
|
+
start: 9823,
|
|
2992
|
+
end: 9823,
|
|
2993
|
+
version: "11.0",
|
|
2994
|
+
name: "chess pawn"
|
|
2995
|
+
},
|
|
2996
|
+
{
|
|
2997
|
+
start: 9854,
|
|
2998
|
+
end: 9854,
|
|
2999
|
+
version: "11.0",
|
|
3000
|
+
name: "infinity"
|
|
3001
|
+
},
|
|
3002
|
+
{
|
|
3003
|
+
start: 128761,
|
|
3004
|
+
end: 128761,
|
|
3005
|
+
version: "11.0",
|
|
3006
|
+
name: "skateboard"
|
|
3007
|
+
},
|
|
3008
|
+
{
|
|
3009
|
+
start: 129357,
|
|
3010
|
+
end: 129359,
|
|
3011
|
+
version: "11.0",
|
|
3012
|
+
name: "lacrosse..flying disc"
|
|
3013
|
+
},
|
|
3014
|
+
{
|
|
3015
|
+
start: 129388,
|
|
3016
|
+
end: 129392,
|
|
3017
|
+
version: "11.0",
|
|
3018
|
+
name: "leafy green..smiling face with hearts"
|
|
3019
|
+
},
|
|
3020
|
+
{
|
|
3021
|
+
start: 129395,
|
|
3022
|
+
end: 129398,
|
|
3023
|
+
version: "11.0",
|
|
3024
|
+
name: "partying face..cold face"
|
|
3025
|
+
},
|
|
3026
|
+
{
|
|
3027
|
+
start: 129402,
|
|
3028
|
+
end: 129402,
|
|
3029
|
+
version: "11.0",
|
|
3030
|
+
name: "pleading face"
|
|
3031
|
+
},
|
|
3032
|
+
{
|
|
3033
|
+
start: 129404,
|
|
3034
|
+
end: 129407,
|
|
3035
|
+
version: "11.0",
|
|
3036
|
+
name: "lab coat..flat shoe"
|
|
3037
|
+
},
|
|
3038
|
+
{
|
|
3039
|
+
start: 129432,
|
|
3040
|
+
end: 129442,
|
|
3041
|
+
version: "11.0",
|
|
3042
|
+
name: "kangaroo..swan"
|
|
3043
|
+
},
|
|
3044
|
+
{
|
|
3045
|
+
start: 129456,
|
|
3046
|
+
end: 129465,
|
|
3047
|
+
version: "11.0",
|
|
3048
|
+
name: "red hair..supervillain"
|
|
3049
|
+
},
|
|
3050
|
+
{
|
|
3051
|
+
start: 129473,
|
|
3052
|
+
end: 129474,
|
|
3053
|
+
version: "11.0",
|
|
3054
|
+
name: "cupcake..salt"
|
|
3055
|
+
},
|
|
3056
|
+
{
|
|
3057
|
+
start: 129511,
|
|
3058
|
+
end: 129535,
|
|
3059
|
+
version: "11.0",
|
|
3060
|
+
name: "red envelope..nazar amulet"
|
|
3061
|
+
},
|
|
3062
|
+
{
|
|
3063
|
+
start: 128725,
|
|
3064
|
+
end: 128725,
|
|
3065
|
+
version: "12.0",
|
|
3066
|
+
name: "hindu temple"
|
|
3067
|
+
},
|
|
3068
|
+
{
|
|
3069
|
+
start: 128762,
|
|
3070
|
+
end: 128762,
|
|
3071
|
+
version: "12.0",
|
|
3072
|
+
name: "auto rickshaw"
|
|
3073
|
+
},
|
|
3074
|
+
{
|
|
3075
|
+
start: 128992,
|
|
3076
|
+
end: 129003,
|
|
3077
|
+
version: "12.0",
|
|
3078
|
+
name: "orange circle..brown square"
|
|
3079
|
+
},
|
|
3080
|
+
{
|
|
3081
|
+
start: 129293,
|
|
3082
|
+
end: 129295,
|
|
3083
|
+
version: "12.0",
|
|
3084
|
+
name: "white heart..pinching hand"
|
|
3085
|
+
},
|
|
3086
|
+
{
|
|
3087
|
+
start: 129343,
|
|
3088
|
+
end: 129343,
|
|
3089
|
+
version: "12.0",
|
|
3090
|
+
name: "diving mask"
|
|
3091
|
+
},
|
|
3092
|
+
{
|
|
3093
|
+
start: 129393,
|
|
3094
|
+
end: 129393,
|
|
3095
|
+
version: "12.0",
|
|
3096
|
+
name: "yawning face"
|
|
3097
|
+
},
|
|
3098
|
+
{
|
|
3099
|
+
start: 129403,
|
|
3100
|
+
end: 129403,
|
|
3101
|
+
version: "12.0",
|
|
3102
|
+
name: "sari"
|
|
3103
|
+
},
|
|
3104
|
+
{
|
|
3105
|
+
start: 129445,
|
|
3106
|
+
end: 129450,
|
|
3107
|
+
version: "12.0",
|
|
3108
|
+
name: "sloth..oyster"
|
|
3109
|
+
},
|
|
3110
|
+
{
|
|
3111
|
+
start: 129454,
|
|
3112
|
+
end: 129455,
|
|
3113
|
+
version: "12.0",
|
|
3114
|
+
name: "guide dog..white cane"
|
|
3115
|
+
},
|
|
3116
|
+
{
|
|
3117
|
+
start: 129466,
|
|
3118
|
+
end: 129471,
|
|
3119
|
+
version: "12.0",
|
|
3120
|
+
name: "safety vest..mechanical leg"
|
|
3121
|
+
},
|
|
3122
|
+
{
|
|
3123
|
+
start: 129475,
|
|
3124
|
+
end: 129482,
|
|
3125
|
+
version: "12.0",
|
|
3126
|
+
name: "beverage box..ice"
|
|
3127
|
+
},
|
|
3128
|
+
{
|
|
3129
|
+
start: 129485,
|
|
3130
|
+
end: 129487,
|
|
3131
|
+
version: "12.0",
|
|
3132
|
+
name: "person standing..deaf person"
|
|
3133
|
+
},
|
|
3134
|
+
{
|
|
3135
|
+
start: 129648,
|
|
3136
|
+
end: 129651,
|
|
3137
|
+
version: "12.0",
|
|
3138
|
+
name: "ballet shoes..shorts"
|
|
3139
|
+
},
|
|
3140
|
+
{
|
|
3141
|
+
start: 129656,
|
|
3142
|
+
end: 129658,
|
|
3143
|
+
version: "12.0",
|
|
3144
|
+
name: "drop of blood..stethoscope"
|
|
3145
|
+
},
|
|
3146
|
+
{
|
|
3147
|
+
start: 129664,
|
|
3148
|
+
end: 129666,
|
|
3149
|
+
version: "12.0",
|
|
3150
|
+
name: "yo-yo..parachute"
|
|
3151
|
+
},
|
|
3152
|
+
{
|
|
3153
|
+
start: 129680,
|
|
3154
|
+
end: 129685,
|
|
3155
|
+
version: "12.0",
|
|
3156
|
+
name: "ringed planet..banjo"
|
|
3157
|
+
},
|
|
3158
|
+
{
|
|
3159
|
+
start: 9895,
|
|
3160
|
+
end: 9895,
|
|
3161
|
+
version: "13.0",
|
|
3162
|
+
name: "transgender symbol"
|
|
3163
|
+
},
|
|
3164
|
+
{
|
|
3165
|
+
start: 128726,
|
|
3166
|
+
end: 128727,
|
|
3167
|
+
version: "13.0",
|
|
3168
|
+
name: "hut..elevator"
|
|
3169
|
+
},
|
|
3170
|
+
{
|
|
3171
|
+
start: 128763,
|
|
3172
|
+
end: 128764,
|
|
3173
|
+
version: "13.0",
|
|
3174
|
+
name: "pickup truck..roller skate"
|
|
3175
|
+
},
|
|
3176
|
+
{
|
|
3177
|
+
start: 129292,
|
|
3178
|
+
end: 129292,
|
|
3179
|
+
version: "13.0",
|
|
3180
|
+
name: "pinched fingers"
|
|
3181
|
+
},
|
|
3182
|
+
{
|
|
3183
|
+
start: 129394,
|
|
3184
|
+
end: 129394,
|
|
3185
|
+
version: "13.0",
|
|
3186
|
+
name: "smiling face with tear"
|
|
3187
|
+
},
|
|
3188
|
+
{
|
|
3189
|
+
start: 129399,
|
|
3190
|
+
end: 129400,
|
|
3191
|
+
version: "13.0",
|
|
3192
|
+
name: "ninja..disguised face"
|
|
3193
|
+
},
|
|
3194
|
+
{
|
|
3195
|
+
start: 129443,
|
|
3196
|
+
end: 129444,
|
|
3197
|
+
version: "13.0",
|
|
3198
|
+
name: "mammoth..dodo"
|
|
3199
|
+
},
|
|
3200
|
+
{
|
|
3201
|
+
start: 129451,
|
|
3202
|
+
end: 129453,
|
|
3203
|
+
version: "13.0",
|
|
3204
|
+
name: "beaver..seal"
|
|
3205
|
+
},
|
|
3206
|
+
{
|
|
3207
|
+
start: 129483,
|
|
3208
|
+
end: 129483,
|
|
3209
|
+
version: "13.0",
|
|
3210
|
+
name: "bubble tea"
|
|
3211
|
+
},
|
|
3212
|
+
{
|
|
3213
|
+
start: 129652,
|
|
3214
|
+
end: 129652,
|
|
3215
|
+
version: "13.0",
|
|
3216
|
+
name: "thong sandal"
|
|
3217
|
+
},
|
|
3218
|
+
{
|
|
3219
|
+
start: 129667,
|
|
3220
|
+
end: 129670,
|
|
3221
|
+
version: "13.0",
|
|
3222
|
+
name: "boomerang..nesting dolls"
|
|
3223
|
+
},
|
|
3224
|
+
{
|
|
3225
|
+
start: 129686,
|
|
3226
|
+
end: 129704,
|
|
3227
|
+
version: "13.0",
|
|
3228
|
+
name: "military helmet..rock"
|
|
3229
|
+
},
|
|
3230
|
+
{
|
|
3231
|
+
start: 129712,
|
|
3232
|
+
end: 129718,
|
|
3233
|
+
version: "13.0",
|
|
3234
|
+
name: "fly..feather"
|
|
3235
|
+
},
|
|
3236
|
+
{
|
|
3237
|
+
start: 129728,
|
|
3238
|
+
end: 129730,
|
|
3239
|
+
version: "13.0",
|
|
3240
|
+
name: "anatomical heart..people hugging"
|
|
3241
|
+
},
|
|
3242
|
+
{
|
|
3243
|
+
start: 129744,
|
|
3244
|
+
end: 129750,
|
|
3245
|
+
version: "13.0",
|
|
3246
|
+
name: "blueberries..teapot"
|
|
3247
|
+
},
|
|
3248
|
+
{
|
|
3249
|
+
start: 128733,
|
|
3250
|
+
end: 128735,
|
|
3251
|
+
version: "14.0",
|
|
3252
|
+
name: "playground slide..ring buoy"
|
|
3253
|
+
},
|
|
3254
|
+
{
|
|
3255
|
+
start: 129008,
|
|
3256
|
+
end: 129008,
|
|
3257
|
+
version: "14.0",
|
|
3258
|
+
name: "heavy equals sign"
|
|
3259
|
+
},
|
|
3260
|
+
{
|
|
3261
|
+
start: 129401,
|
|
3262
|
+
end: 129401,
|
|
3263
|
+
version: "14.0",
|
|
3264
|
+
name: "face holding back tears"
|
|
3265
|
+
},
|
|
3266
|
+
{
|
|
3267
|
+
start: 129484,
|
|
3268
|
+
end: 129484,
|
|
3269
|
+
version: "14.0",
|
|
3270
|
+
name: "troll"
|
|
3271
|
+
},
|
|
3272
|
+
{
|
|
3273
|
+
start: 129659,
|
|
3274
|
+
end: 129660,
|
|
3275
|
+
version: "14.0",
|
|
3276
|
+
name: "x-ray..crutch"
|
|
3277
|
+
},
|
|
3278
|
+
{
|
|
3279
|
+
start: 129705,
|
|
3280
|
+
end: 129708,
|
|
3281
|
+
version: "14.0",
|
|
3282
|
+
name: "mirror ball..hamsa"
|
|
3283
|
+
},
|
|
3284
|
+
{
|
|
3285
|
+
start: 129719,
|
|
3286
|
+
end: 129722,
|
|
3287
|
+
version: "14.0",
|
|
3288
|
+
name: "lotus..nest with eggs"
|
|
3289
|
+
},
|
|
3290
|
+
{
|
|
3291
|
+
start: 129731,
|
|
3292
|
+
end: 129733,
|
|
3293
|
+
version: "14.0",
|
|
3294
|
+
name: "pregnant man..person with crown"
|
|
3295
|
+
},
|
|
3296
|
+
{
|
|
3297
|
+
start: 129751,
|
|
3298
|
+
end: 129753,
|
|
3299
|
+
version: "14.0",
|
|
3300
|
+
name: "pouring liquid..jar"
|
|
3301
|
+
},
|
|
3302
|
+
{
|
|
3303
|
+
start: 129760,
|
|
3304
|
+
end: 129767,
|
|
3305
|
+
version: "14.0",
|
|
3306
|
+
name: "melting face..bubbles"
|
|
3307
|
+
},
|
|
3308
|
+
{
|
|
3309
|
+
start: 129776,
|
|
3310
|
+
end: 129782,
|
|
3311
|
+
version: "14.0",
|
|
3312
|
+
name: "hand with index finger and thumb crossed..heart hands"
|
|
3313
|
+
},
|
|
3314
|
+
{
|
|
3315
|
+
start: 128732,
|
|
3316
|
+
end: 128732,
|
|
3317
|
+
version: "15.0",
|
|
3318
|
+
name: "wireless"
|
|
3319
|
+
},
|
|
3320
|
+
{
|
|
3321
|
+
start: 129653,
|
|
3322
|
+
end: 129655,
|
|
3323
|
+
version: "15.0",
|
|
3324
|
+
name: "light blue heart..pink heart"
|
|
3325
|
+
},
|
|
3326
|
+
{
|
|
3327
|
+
start: 129671,
|
|
3328
|
+
end: 129672,
|
|
3329
|
+
version: "15.0",
|
|
3330
|
+
name: "maracas..flute"
|
|
3331
|
+
},
|
|
3332
|
+
{
|
|
3333
|
+
start: 129709,
|
|
3334
|
+
end: 129711,
|
|
3335
|
+
version: "15.0",
|
|
3336
|
+
name: "folding hand fan..khanda"
|
|
3337
|
+
},
|
|
3338
|
+
{
|
|
3339
|
+
start: 129723,
|
|
3340
|
+
end: 129725,
|
|
3341
|
+
version: "15.0",
|
|
3342
|
+
name: "hyacinth..wing"
|
|
3343
|
+
},
|
|
3344
|
+
{
|
|
3345
|
+
start: 129727,
|
|
3346
|
+
end: 129727,
|
|
3347
|
+
version: "15.0",
|
|
3348
|
+
name: "goose"
|
|
3349
|
+
},
|
|
3350
|
+
{
|
|
3351
|
+
start: 129742,
|
|
3352
|
+
end: 129743,
|
|
3353
|
+
version: "15.0",
|
|
3354
|
+
name: "moose..donkey"
|
|
3355
|
+
},
|
|
3356
|
+
{
|
|
3357
|
+
start: 129754,
|
|
3358
|
+
end: 129755,
|
|
3359
|
+
version: "15.0",
|
|
3360
|
+
name: "ginger root..pea pod"
|
|
3361
|
+
},
|
|
3362
|
+
{
|
|
3363
|
+
start: 129768,
|
|
3364
|
+
end: 129768,
|
|
3365
|
+
version: "15.0",
|
|
3366
|
+
name: "shaking face"
|
|
3367
|
+
},
|
|
3368
|
+
{
|
|
3369
|
+
start: 129783,
|
|
3370
|
+
end: 129784,
|
|
3371
|
+
version: "15.0",
|
|
3372
|
+
name: "leftwards pushing hand..rightwards pushing hand"
|
|
3373
|
+
},
|
|
3374
|
+
{
|
|
3375
|
+
start: 129673,
|
|
3376
|
+
end: 129673,
|
|
3377
|
+
version: "16.0",
|
|
3378
|
+
name: "harp"
|
|
3379
|
+
},
|
|
3380
|
+
{
|
|
3381
|
+
start: 129679,
|
|
3382
|
+
end: 129679,
|
|
3383
|
+
version: "16.0",
|
|
3384
|
+
name: "shovel"
|
|
3385
|
+
},
|
|
3386
|
+
{
|
|
3387
|
+
start: 129726,
|
|
3388
|
+
end: 129726,
|
|
3389
|
+
version: "16.0",
|
|
3390
|
+
name: "leafless tree"
|
|
3391
|
+
},
|
|
3392
|
+
{
|
|
3393
|
+
start: 129734,
|
|
3394
|
+
end: 129734,
|
|
3395
|
+
version: "16.0",
|
|
3396
|
+
name: "fingerprint"
|
|
3397
|
+
},
|
|
3398
|
+
{
|
|
3399
|
+
start: 129756,
|
|
3400
|
+
end: 129756,
|
|
3401
|
+
version: "16.0",
|
|
3402
|
+
name: "root vegetable"
|
|
3403
|
+
},
|
|
3404
|
+
{
|
|
3405
|
+
start: 129759,
|
|
3406
|
+
end: 129759,
|
|
3407
|
+
version: "16.0",
|
|
3408
|
+
name: "splatter"
|
|
3409
|
+
},
|
|
3410
|
+
{
|
|
3411
|
+
start: 129769,
|
|
3412
|
+
end: 129769,
|
|
3413
|
+
version: "16.0",
|
|
3414
|
+
name: "face with bags under eyes"
|
|
3415
|
+
},
|
|
3416
|
+
{
|
|
3417
|
+
start: 128728,
|
|
3418
|
+
end: 128728,
|
|
3419
|
+
version: "17.0",
|
|
3420
|
+
name: "landslide"
|
|
3421
|
+
},
|
|
3422
|
+
{
|
|
3423
|
+
start: 129674,
|
|
3424
|
+
end: 129674,
|
|
3425
|
+
version: "17.0",
|
|
3426
|
+
name: "trombone"
|
|
3427
|
+
},
|
|
3428
|
+
{
|
|
3429
|
+
start: 129678,
|
|
3430
|
+
end: 129678,
|
|
3431
|
+
version: "17.0",
|
|
3432
|
+
name: "treasure chest"
|
|
3433
|
+
},
|
|
3434
|
+
{
|
|
3435
|
+
start: 129736,
|
|
3436
|
+
end: 129736,
|
|
3437
|
+
version: "17.0",
|
|
3438
|
+
name: "hairy creature"
|
|
3439
|
+
},
|
|
3440
|
+
{
|
|
3441
|
+
start: 129741,
|
|
3442
|
+
end: 129741,
|
|
3443
|
+
version: "17.0",
|
|
3444
|
+
name: "orca"
|
|
3445
|
+
},
|
|
3446
|
+
{
|
|
3447
|
+
start: 129770,
|
|
3448
|
+
end: 129770,
|
|
3449
|
+
version: "17.0",
|
|
3450
|
+
name: "distorted face"
|
|
3451
|
+
},
|
|
3452
|
+
{
|
|
3453
|
+
start: 129775,
|
|
3454
|
+
end: 129775,
|
|
3455
|
+
version: "17.0",
|
|
3456
|
+
name: "fight cloud"
|
|
3457
|
+
}
|
|
3458
|
+
];
|
|
3459
|
+
//#endregion
|
|
3460
|
+
//#region packages/eslint-plugin-formatjs/emoji-utils.ts
|
|
3461
|
+
/**
|
|
3462
|
+
* Emoji detection utilities using Intl.Segmenter
|
|
3463
|
+
*/
|
|
3464
|
+
/**
|
|
3465
|
+
* Cached Intl.Segmenter instance for emoji extraction
|
|
3466
|
+
* Reused across all extractEmojis calls for better performance
|
|
3467
|
+
*/
|
|
3468
|
+
const graphemeSegmenter = new Intl.Segmenter("en", { granularity: "grapheme" });
|
|
3469
|
+
/**
|
|
3470
|
+
* Regex for detecting emoji using Unicode 17.0.0 Emoji_Presentation data
|
|
3471
|
+
* Generated from @unicode/unicode-17.0.0/Binary_Property/Emoji_Presentation
|
|
3472
|
+
* This avoids false positives from #, *, digits, and text symbols like ©
|
|
3473
|
+
*/
|
|
3474
|
+
const emojiPresentationRegexCompiled = emojiPresentationRegex.default ?? emojiPresentationRegex;
|
|
3475
|
+
/**
|
|
3476
|
+
* Regex for detecting emoji-capable characters using Unicode 17.0.0 Emoji property
|
|
3477
|
+
* This includes characters that can have emoji presentation (like ❤, ☀)
|
|
3478
|
+
*/
|
|
3479
|
+
const emojiPropertyRegexCompiled = emojiPropertyRegex.default ?? emojiPropertyRegex;
|
|
3480
|
+
/**
|
|
3481
|
+
* Check if a string contains any emoji
|
|
3482
|
+
* Uses Unicode 17.0.0 Emoji_Presentation data and variation selector (U+FE0F)
|
|
3483
|
+
* to properly detect emoji while avoiding false positives from characters like
|
|
3484
|
+
* #, *, digits 0-9, and text symbols like © which have \p{Emoji} but are not visual emoji
|
|
3485
|
+
*/
|
|
3486
|
+
function hasEmoji(text) {
|
|
3487
|
+
if (emojiPresentationRegexCompiled.test(text)) return true;
|
|
3488
|
+
if (text.includes("️")) {
|
|
3489
|
+
const segments = graphemeSegmenter.segment(text);
|
|
3490
|
+
for (const { segment } of segments) if (segment.includes("️")) {
|
|
3491
|
+
const beforeVS = segment.replace(/\uFE0F/g, "");
|
|
3492
|
+
if (emojiPropertyRegexCompiled.test(beforeVS)) return true;
|
|
3493
|
+
}
|
|
3494
|
+
}
|
|
3495
|
+
return false;
|
|
3496
|
+
}
|
|
3497
|
+
/**
|
|
3498
|
+
* Extract all emoji from a string using Intl.Segmenter
|
|
3499
|
+
* Returns an array of emoji characters (including multi-codepoint sequences)
|
|
3500
|
+
*
|
|
3501
|
+
* Uses grapheme segmentation to properly handle:
|
|
3502
|
+
* - Emoji with skin tone modifiers (🙋🏻)
|
|
3503
|
+
* - ZWJ sequences (👨👩👧👦)
|
|
3504
|
+
* - Flag sequences (🇺🇸)
|
|
3505
|
+
* - Regional indicator sequences
|
|
3506
|
+
* - Any other complex emoji grapheme clusters
|
|
3507
|
+
*/
|
|
3508
|
+
function extractEmojis(text) {
|
|
3509
|
+
const segments = graphemeSegmenter.segment(text);
|
|
3510
|
+
const emojis = [];
|
|
3511
|
+
for (const { segment } of segments) {
|
|
3512
|
+
if (emojiPresentationRegexCompiled.test(segment)) {
|
|
3513
|
+
emojis.push(segment);
|
|
3514
|
+
continue;
|
|
3515
|
+
}
|
|
3516
|
+
if (segment.includes("️")) {
|
|
3517
|
+
const beforeVS = segment.replace(/\uFE0F/g, "");
|
|
3518
|
+
if (emojiPropertyRegexCompiled.test(beforeVS)) emojis.push(segment);
|
|
3519
|
+
}
|
|
3520
|
+
}
|
|
3521
|
+
return emojis;
|
|
3522
|
+
}
|
|
3523
|
+
/**
|
|
3524
|
+
* Check if a version string is a valid emoji version
|
|
3525
|
+
*/
|
|
3526
|
+
function isValidEmojiVersion(version) {
|
|
3527
|
+
return EMOJI_VERSIONS.includes(version);
|
|
3528
|
+
}
|
|
3529
|
+
/**
|
|
3530
|
+
* Get the Unicode version for a given emoji character
|
|
3531
|
+
* Returns undefined if the emoji is not found in our data
|
|
3532
|
+
*/
|
|
3533
|
+
function getEmojiVersion(emoji) {
|
|
3534
|
+
const codepoint = emoji.codePointAt(0);
|
|
3535
|
+
if (codepoint === void 0) return;
|
|
3536
|
+
for (const range of EMOJI_RANGES) if (codepoint >= range.start && codepoint <= range.end) return range.version;
|
|
3537
|
+
}
|
|
3538
|
+
/**
|
|
3539
|
+
* Filter function for emoji versions
|
|
3540
|
+
* Returns a filter function that checks if an emoji is from version or below
|
|
3541
|
+
*/
|
|
3542
|
+
function filterEmojis(version) {
|
|
3543
|
+
const maxVersion = parseVersion(version);
|
|
3544
|
+
return (emoji) => {
|
|
3545
|
+
const emojiVersion = getEmojiVersion(emoji);
|
|
3546
|
+
if (!emojiVersion) return true;
|
|
3547
|
+
return parseVersion(emojiVersion) <= maxVersion;
|
|
3548
|
+
};
|
|
3549
|
+
}
|
|
3550
|
+
/**
|
|
3551
|
+
* Parse version string to comparable number
|
|
3552
|
+
* e.g., "12.0" -> 12.0, "13.1" -> 13.1
|
|
3553
|
+
*/
|
|
3554
|
+
function parseVersion(version) {
|
|
3555
|
+
return parseFloat(version);
|
|
3556
|
+
}
|
|
3557
|
+
//#endregion
|
|
3558
|
+
//#region packages/eslint-plugin-formatjs/rules/no-emoji.ts
|
|
3559
|
+
const name$14 = "no-emoji";
|
|
3560
|
+
function checkNode$9(context, node) {
|
|
3561
|
+
const msgs = extractMessages(node, getSettings(context));
|
|
3562
|
+
let versionAbove;
|
|
3563
|
+
const [emojiConfig] = context.options;
|
|
3564
|
+
if (emojiConfig?.versionAbove && isValidEmojiVersion(emojiConfig.versionAbove)) versionAbove = emojiConfig.versionAbove;
|
|
3565
|
+
for (const [{ message: { defaultMessage }, messageNode }] of msgs) {
|
|
3566
|
+
if (!defaultMessage || !messageNode) continue;
|
|
3567
|
+
if (hasEmoji(defaultMessage)) if (versionAbove) {
|
|
3568
|
+
const filter = filterEmojis(versionAbove);
|
|
3569
|
+
for (const emoji of extractEmojis(defaultMessage)) if (!filter(emoji)) context.report({
|
|
3570
|
+
node: messageNode,
|
|
3571
|
+
messageId: "notAllowedAboveVersion",
|
|
3572
|
+
data: {
|
|
3573
|
+
versionAbove,
|
|
3574
|
+
emoji
|
|
3575
|
+
}
|
|
3576
|
+
});
|
|
3577
|
+
} else context.report({
|
|
3578
|
+
node: messageNode,
|
|
3579
|
+
messageId: "notAllowed"
|
|
3580
|
+
});
|
|
3581
|
+
}
|
|
3582
|
+
}
|
|
3583
|
+
const rule$12 = {
|
|
3584
|
+
meta: {
|
|
3585
|
+
type: "problem",
|
|
3586
|
+
docs: {
|
|
3587
|
+
description: "Disallow emojis in message",
|
|
3588
|
+
url: "https://formatjs.github.io/docs/tooling/linter#no-emoji"
|
|
3589
|
+
},
|
|
3590
|
+
fixable: "code",
|
|
3591
|
+
schema: [{
|
|
3592
|
+
type: "object",
|
|
3593
|
+
properties: { versionAbove: {
|
|
3594
|
+
type: "string",
|
|
3595
|
+
enum: [
|
|
3596
|
+
"0.6",
|
|
3597
|
+
"0.7",
|
|
3598
|
+
"1.0",
|
|
3599
|
+
"2.0",
|
|
3600
|
+
"3.0",
|
|
3601
|
+
"4.0",
|
|
3602
|
+
"5.0",
|
|
3603
|
+
"11.0",
|
|
3604
|
+
"12.0",
|
|
3605
|
+
"13.0",
|
|
3606
|
+
"14.0",
|
|
3607
|
+
"15.0",
|
|
3608
|
+
"16.0",
|
|
3609
|
+
"17.0"
|
|
3610
|
+
]
|
|
3611
|
+
} },
|
|
3612
|
+
additionalProperties: false
|
|
3613
|
+
}],
|
|
3614
|
+
messages: {
|
|
3615
|
+
notAllowed: "Emojis are not allowed",
|
|
3616
|
+
notAllowedAboveVersion: "Emojis above version {{versionAbove}} are not allowed - Emoji: {{emoji}}"
|
|
3617
|
+
}
|
|
3618
|
+
},
|
|
3619
|
+
create(context) {
|
|
3620
|
+
const callExpressionVisitor = (node) => checkNode$9(context, node);
|
|
3621
|
+
const parserServices = context.sourceCode.parserServices;
|
|
3622
|
+
if (parserServices?.defineTemplateBodyVisitor) return parserServices.defineTemplateBodyVisitor({ CallExpression: callExpressionVisitor }, { CallExpression: callExpressionVisitor });
|
|
3623
|
+
return {
|
|
3624
|
+
JSXOpeningElement: (node) => checkNode$9(context, node),
|
|
3625
|
+
CallExpression: callExpressionVisitor
|
|
3626
|
+
};
|
|
3627
|
+
}
|
|
3628
|
+
};
|
|
3629
|
+
//#endregion
|
|
3630
|
+
//#region packages/eslint-plugin-formatjs/rules/no-id.ts
|
|
3631
|
+
function isComment(token) {
|
|
3632
|
+
return !!token && (token.type === "Block" || token.type === "Line");
|
|
3633
|
+
}
|
|
3634
|
+
const name$13 = "no-id";
|
|
3635
|
+
function checkNode$8(context, node) {
|
|
3636
|
+
const msgs = extractMessages(node, getSettings(context));
|
|
3637
|
+
for (const [{ idPropNode }] of msgs) if (idPropNode) context.report({
|
|
3638
|
+
node: idPropNode,
|
|
3639
|
+
messageId: "noId",
|
|
3640
|
+
fix(fixer) {
|
|
3641
|
+
const token = context.sourceCode.getTokenAfter(idPropNode);
|
|
3642
|
+
const fixes = [fixer.remove(idPropNode)];
|
|
3643
|
+
if (token && !isComment(token) && token?.value === ",") fixes.push(fixer.remove(token));
|
|
3644
|
+
return fixes;
|
|
3645
|
+
}
|
|
3646
|
+
});
|
|
3647
|
+
}
|
|
3648
|
+
const rule$11 = {
|
|
3649
|
+
meta: {
|
|
3650
|
+
type: "problem",
|
|
3651
|
+
docs: {
|
|
3652
|
+
description: "Ban explicit ID from MessageDescriptor",
|
|
3653
|
+
url: "https://formatjs.github.io/docs/tooling/linter#no-id"
|
|
3654
|
+
},
|
|
3655
|
+
fixable: "code",
|
|
3656
|
+
schema: [],
|
|
3657
|
+
messages: { noId: "Manual `id` are not allowed in message descriptor" }
|
|
3658
|
+
},
|
|
3659
|
+
create(context) {
|
|
3660
|
+
const callExpressionVisitor = (node) => checkNode$8(context, node);
|
|
3661
|
+
const parserServices = context.sourceCode.parserServices;
|
|
3662
|
+
if (parserServices?.defineTemplateBodyVisitor) return parserServices.defineTemplateBodyVisitor({ CallExpression: callExpressionVisitor }, { CallExpression: callExpressionVisitor });
|
|
3663
|
+
return {
|
|
3664
|
+
JSXOpeningElement: (node) => checkNode$8(context, node),
|
|
3665
|
+
CallExpression: callExpressionVisitor
|
|
3666
|
+
};
|
|
3667
|
+
}
|
|
3668
|
+
};
|
|
3669
|
+
//#endregion
|
|
3670
|
+
//#region packages/eslint-plugin-formatjs/rules/no-invalid-icu.ts
|
|
3671
|
+
const name$12 = "no-invalid-icu";
|
|
3672
|
+
function checkNode$7(context, node) {
|
|
3673
|
+
const settings = getSettings(context);
|
|
3674
|
+
let msgs;
|
|
3675
|
+
try {
|
|
3676
|
+
msgs = extractMessages(node, settings);
|
|
3677
|
+
} catch (e) {
|
|
3678
|
+
context.report({
|
|
3679
|
+
node,
|
|
3680
|
+
messageId: "parseError",
|
|
3681
|
+
data: { error: e.message }
|
|
3682
|
+
});
|
|
3683
|
+
return;
|
|
3684
|
+
}
|
|
3685
|
+
if (!msgs.length) return;
|
|
3686
|
+
for (const [{ message: { defaultMessage }, messageNode }] of msgs) {
|
|
3687
|
+
if (!defaultMessage || !messageNode) continue;
|
|
3688
|
+
try {
|
|
3689
|
+
parse(defaultMessage, { ignoreTag: settings.ignoreTag });
|
|
3690
|
+
} catch (e) {
|
|
3691
|
+
context.report({
|
|
3692
|
+
node: messageNode,
|
|
3693
|
+
messageId: "parseError",
|
|
3694
|
+
data: { error: e.message }
|
|
3695
|
+
});
|
|
3696
|
+
}
|
|
3697
|
+
}
|
|
3698
|
+
}
|
|
3699
|
+
const rule$10 = {
|
|
3700
|
+
meta: {
|
|
3701
|
+
type: "problem",
|
|
3702
|
+
docs: { description: `Make sure ICU messages are formatted correctly with no bad select statements, plurals, etc.` },
|
|
3703
|
+
fixable: "code",
|
|
3704
|
+
schema: [],
|
|
3705
|
+
messages: CORE_MESSAGES
|
|
3706
|
+
},
|
|
3707
|
+
create(context) {
|
|
3708
|
+
const callExpressionVisitor = (node) => checkNode$7(context, node);
|
|
3709
|
+
const parserServices = context.sourceCode.parserServices;
|
|
3710
|
+
if (parserServices?.defineTemplateBodyVisitor) return parserServices.defineTemplateBodyVisitor({ CallExpression: callExpressionVisitor }, { CallExpression: callExpressionVisitor });
|
|
3711
|
+
return {
|
|
3712
|
+
JSXOpeningElement: (node) => checkNode$7(context, node),
|
|
3713
|
+
CallExpression: callExpressionVisitor
|
|
3714
|
+
};
|
|
3715
|
+
}
|
|
3716
|
+
};
|
|
3717
|
+
//#endregion
|
|
3718
|
+
//#region packages/eslint-plugin-formatjs/rules/no-literal-string-in-jsx.ts
|
|
3719
|
+
const picomatch = picomatchNs.default ?? picomatchNs;
|
|
3720
|
+
const propMatcherSchema = {
|
|
3721
|
+
type: "array",
|
|
3722
|
+
items: {
|
|
3723
|
+
type: "array",
|
|
3724
|
+
items: [{ type: "string" }, { type: "string" }]
|
|
3725
|
+
}
|
|
3726
|
+
};
|
|
3727
|
+
const defaultPropIncludePattern = [
|
|
3728
|
+
["*", "aria-{label,description,details,errormessage}"],
|
|
3729
|
+
["[a-z]*([a-z0-9])", "(placeholder|title)"],
|
|
3730
|
+
["img", "alt"]
|
|
3731
|
+
];
|
|
3732
|
+
const defaultPropExcludePattern = [];
|
|
3733
|
+
function stringifyJsxTagName(tagName) {
|
|
3734
|
+
switch (tagName.type) {
|
|
3735
|
+
case "JSXIdentifier": return tagName.name;
|
|
3736
|
+
case "JSXMemberExpression": return `${stringifyJsxTagName(tagName.object)}.${tagName.property.name}`;
|
|
3737
|
+
case "JSXNamespacedName": return `${tagName.namespace.name}:${tagName.name.name}`;
|
|
3738
|
+
}
|
|
3739
|
+
}
|
|
3740
|
+
function compilePropMatcher(propMatcher) {
|
|
3741
|
+
return propMatcher.map(([tagNamePattern, propNamePattern]) => {
|
|
3742
|
+
return [picomatch.makeRe(tagNamePattern, { contains: false }), picomatch.makeRe(propNamePattern, { contains: false })];
|
|
3743
|
+
});
|
|
3744
|
+
}
|
|
3745
|
+
const name$11 = "no-literal-string-in-jsx";
|
|
3746
|
+
const rule$9 = {
|
|
3747
|
+
meta: {
|
|
3748
|
+
type: "problem",
|
|
3749
|
+
docs: {
|
|
3750
|
+
description: "Disallow untranslated literal strings without translation.",
|
|
3751
|
+
url: "https://formatjs.github.io/docs/tooling/linter#no-literal-string-in-jsx"
|
|
3752
|
+
},
|
|
3753
|
+
schema: [{
|
|
3754
|
+
type: "object",
|
|
3755
|
+
additionalProperties: false,
|
|
3756
|
+
properties: { props: {
|
|
3757
|
+
type: "object",
|
|
3758
|
+
additionalProperties: false,
|
|
3759
|
+
properties: {
|
|
3760
|
+
include: { ...propMatcherSchema },
|
|
3761
|
+
exclude: { ...propMatcherSchema }
|
|
3762
|
+
}
|
|
3763
|
+
} }
|
|
3764
|
+
}],
|
|
3765
|
+
messages: { noLiteralStringInJsx: "Cannot have untranslated text in JSX" }
|
|
3766
|
+
},
|
|
3767
|
+
create(context) {
|
|
3768
|
+
const userConfig = context.options[0] || {};
|
|
3769
|
+
const propIncludePattern = compilePropMatcher([...defaultPropIncludePattern, ...userConfig.props?.include ?? []]);
|
|
3770
|
+
const propExcludePattern = compilePropMatcher([...defaultPropExcludePattern, ...userConfig.props?.exclude ?? []]);
|
|
3771
|
+
const lexicalJsxStack = [];
|
|
3772
|
+
const shouldSkipCurrentJsxElement = () => {
|
|
3773
|
+
const currentJsxNode = lexicalJsxStack[lexicalJsxStack.length - 1];
|
|
3774
|
+
if (currentJsxNode.type === "JSXFragment") return false;
|
|
3775
|
+
const nameString = stringifyJsxTagName(currentJsxNode.openingElement.name);
|
|
3776
|
+
for (const [tagNamePattern, propNamePattern] of propExcludePattern) if (tagNamePattern.test(nameString) && propNamePattern.test("children")) return true;
|
|
3777
|
+
return false;
|
|
3778
|
+
};
|
|
3779
|
+
const shouldSkipCurrentJsxAttribute = (node) => {
|
|
3780
|
+
const currentJsxNode = lexicalJsxStack[lexicalJsxStack.length - 1];
|
|
3781
|
+
if (currentJsxNode.type === "JSXFragment") return false;
|
|
3782
|
+
const nameString = stringifyJsxTagName(currentJsxNode.openingElement.name);
|
|
3783
|
+
const attributeName = typeof node.name.name === "string" ? node.name.name : node.name.name.name;
|
|
3784
|
+
for (const [tagNamePattern, propNamePattern] of propExcludePattern) if (tagNamePattern.test(nameString) && propNamePattern.test(attributeName)) return true;
|
|
3785
|
+
for (const [tagNamePattern, propNamePattern] of propIncludePattern) if (tagNamePattern.test(nameString) && propNamePattern.test(attributeName)) return false;
|
|
3786
|
+
return true;
|
|
3787
|
+
};
|
|
3788
|
+
const checkJSXExpression = (node) => {
|
|
3789
|
+
if (node.type === "Literal" && typeof node.value === "string" && node.value.length > 0 || node.type === "TemplateLiteral" && (node.quasis.length > 1 || node.quasis[0].value.raw.length > 0)) context.report({
|
|
3790
|
+
node,
|
|
3791
|
+
messageId: "noLiteralStringInJsx"
|
|
3792
|
+
});
|
|
3793
|
+
else if (node.type === "BinaryExpression" && node.operator === "+") {
|
|
3794
|
+
checkJSXExpression(node.left);
|
|
3795
|
+
checkJSXExpression(node.right);
|
|
3796
|
+
} else if (node.type === "ConditionalExpression") {
|
|
3797
|
+
checkJSXExpression(node.consequent);
|
|
3798
|
+
checkJSXExpression(node.alternate);
|
|
3799
|
+
} else if (node.type === "LogicalExpression") {
|
|
3800
|
+
checkJSXExpression(node.left);
|
|
3801
|
+
checkJSXExpression(node.right);
|
|
3802
|
+
}
|
|
3803
|
+
};
|
|
3804
|
+
return {
|
|
3805
|
+
JSXElement: (node) => {
|
|
3806
|
+
lexicalJsxStack.push(node);
|
|
3807
|
+
},
|
|
3808
|
+
"JSXElement:exit": () => {
|
|
3809
|
+
lexicalJsxStack.pop();
|
|
3810
|
+
},
|
|
3811
|
+
JSXFragment: (node) => {
|
|
3812
|
+
lexicalJsxStack.push(node);
|
|
3813
|
+
},
|
|
3814
|
+
"JSXFragment:exit": () => {
|
|
3815
|
+
lexicalJsxStack.pop();
|
|
3816
|
+
},
|
|
3817
|
+
JSXAttribute: (node) => {
|
|
3818
|
+
if (shouldSkipCurrentJsxAttribute(node)) return;
|
|
3819
|
+
if (!node.value) return;
|
|
3820
|
+
if (node.value.type === "Literal" && typeof node.value.value === "string" && node.value.value.length > 0) context.report({
|
|
3821
|
+
node,
|
|
3822
|
+
messageId: "noLiteralStringInJsx"
|
|
3823
|
+
});
|
|
3824
|
+
else if (node.value.type === "JSXExpressionContainer" && node.value.expression.type !== "JSXEmptyExpression") checkJSXExpression(node.value.expression);
|
|
3825
|
+
},
|
|
3826
|
+
JSXText: (node) => {
|
|
3827
|
+
if (!node.value.replace(/\s*/gm, "")) return;
|
|
3828
|
+
if (shouldSkipCurrentJsxElement()) return;
|
|
3829
|
+
context.report({
|
|
3830
|
+
node,
|
|
3831
|
+
messageId: "noLiteralStringInJsx"
|
|
3832
|
+
});
|
|
3833
|
+
},
|
|
3834
|
+
"JSXElement > JSXExpressionContainer": (node) => {
|
|
3835
|
+
if (shouldSkipCurrentJsxElement()) return;
|
|
3836
|
+
if (node.expression.type !== "JSXEmptyExpression") checkJSXExpression(node.expression);
|
|
3837
|
+
}
|
|
3838
|
+
};
|
|
3839
|
+
}
|
|
3840
|
+
};
|
|
3841
|
+
//#endregion
|
|
3842
|
+
//#region packages/eslint-plugin-formatjs/rules/no-missing-icu-plural-one-placeholders.ts
|
|
3843
|
+
const name$10 = "no-missing-icu-plural-one-placeholders";
|
|
3844
|
+
function verifyAst$4(context, messageNode, ast) {
|
|
3845
|
+
const patches = [];
|
|
3846
|
+
_verifyAstAndReplace(ast);
|
|
3847
|
+
if (patches.length > 0) {
|
|
3848
|
+
const patchedMessage = patchMessage(messageNode, ast, (content) => {
|
|
3849
|
+
return patches.reduce((magicString, patch) => {
|
|
3850
|
+
switch (patch.type) {
|
|
3851
|
+
case "prependLeft": return magicString.prependLeft(patch.index, patch.content);
|
|
3852
|
+
case "remove": return magicString.remove(patch.start, patch.end);
|
|
3853
|
+
case "update": return magicString.update(patch.start, patch.end, patch.content);
|
|
3854
|
+
}
|
|
3855
|
+
}, new MagicString(content)).toString();
|
|
3856
|
+
});
|
|
3857
|
+
context.report({
|
|
3858
|
+
node: messageNode,
|
|
3859
|
+
messageId: "noMissingIcuPluralOnePlaceholders",
|
|
3860
|
+
fix: patchedMessage !== null ? (fixer) => fixer.replaceText(messageNode, patchedMessage) : null
|
|
3861
|
+
});
|
|
3862
|
+
}
|
|
3863
|
+
function _verifyAstAndReplace(ast, root = true) {
|
|
3864
|
+
for (const el of ast) if (isPluralElement(el) && el.options["one"]) _verifyAstAndReplace(el.options["one"].value, false);
|
|
3865
|
+
else if (isSelectElement(el)) for (const { value } of Object.values(el.options)) _verifyAstAndReplace(value, root);
|
|
3866
|
+
else if (isTagElement(el)) _verifyAstAndReplace(el.children, root);
|
|
3867
|
+
else if (!root && isLiteralElement(el)) {
|
|
3868
|
+
const match = el.value.match(/\b1\b/);
|
|
3869
|
+
if (match && el.location) patches.push({
|
|
3870
|
+
type: "update",
|
|
3871
|
+
start: el.location.start.offset,
|
|
3872
|
+
end: el.location.end.offset,
|
|
3873
|
+
content: el.value.replace(match[0], "#")
|
|
3874
|
+
});
|
|
3875
|
+
}
|
|
3876
|
+
}
|
|
3877
|
+
}
|
|
3878
|
+
function checkNode$6(context, node) {
|
|
3879
|
+
const msgs = extractMessages(node);
|
|
3880
|
+
for (const [{ message: { defaultMessage }, messageNode }] of msgs) {
|
|
3881
|
+
if (!defaultMessage || !messageNode) continue;
|
|
3882
|
+
let ast;
|
|
3883
|
+
try {
|
|
3884
|
+
ast = parse(defaultMessage, { captureLocation: true });
|
|
3885
|
+
} catch (e) {
|
|
3886
|
+
context.report({
|
|
3887
|
+
node: messageNode,
|
|
3888
|
+
messageId: "parseError",
|
|
3889
|
+
data: { error: e.message }
|
|
3890
|
+
});
|
|
3891
|
+
continue;
|
|
3892
|
+
}
|
|
3893
|
+
verifyAst$4(context, messageNode, ast);
|
|
3894
|
+
}
|
|
3895
|
+
}
|
|
3896
|
+
const rule$8 = {
|
|
3897
|
+
meta: {
|
|
3898
|
+
type: "problem",
|
|
3899
|
+
docs: {
|
|
3900
|
+
description: "We use `one {# item}` instead of `one {1 item}` in ICU messages as some locales use the `one` formatting for other similar numbers.",
|
|
3901
|
+
url: "https://formatjs.github.io/docs/tooling/linter#no-explicit-icu-plural"
|
|
3902
|
+
},
|
|
3903
|
+
fixable: "code",
|
|
3904
|
+
messages: {
|
|
3905
|
+
...CORE_MESSAGES,
|
|
3906
|
+
noMissingIcuPluralOnePlaceholders: "Use `one {# item}` instead of `one {1 item}` in ICU messages."
|
|
3907
|
+
},
|
|
3908
|
+
schema: []
|
|
3909
|
+
},
|
|
3910
|
+
create(context) {
|
|
3911
|
+
const callExpressionVisitor = (node) => checkNode$6(context, node);
|
|
3912
|
+
const parserServices = context.sourceCode.parserServices;
|
|
3913
|
+
if (parserServices?.defineTemplateBodyVisitor) return parserServices.defineTemplateBodyVisitor({ CallExpression: callExpressionVisitor }, { CallExpression: callExpressionVisitor });
|
|
3914
|
+
return {
|
|
3915
|
+
JSXOpeningElement: (node) => checkNode$6(context, node),
|
|
3916
|
+
CallExpression: callExpressionVisitor
|
|
3917
|
+
};
|
|
3918
|
+
}
|
|
3919
|
+
};
|
|
3920
|
+
//#endregion
|
|
3921
|
+
//#region packages/eslint-plugin-formatjs/rules/no-multiple-plurals.ts
|
|
3922
|
+
function verifyAst$3(ast, pluralCount = { count: 0 }) {
|
|
3923
|
+
const errors = [];
|
|
3924
|
+
for (const el of ast) if (isPluralElement(el)) {
|
|
3925
|
+
pluralCount.count++;
|
|
3926
|
+
if (pluralCount.count > 1) errors.push({
|
|
3927
|
+
messageId: "noMultiplePlurals",
|
|
3928
|
+
data: {}
|
|
3929
|
+
});
|
|
3930
|
+
const { options } = el;
|
|
3931
|
+
for (const selector of Object.keys(options)) errors.push(...verifyAst$3(options[selector].value, pluralCount));
|
|
3932
|
+
}
|
|
3933
|
+
return errors;
|
|
3934
|
+
}
|
|
3935
|
+
function checkNode$5(context, node) {
|
|
3936
|
+
const settings = getSettings(context);
|
|
3937
|
+
const msgs = extractMessages(node, settings);
|
|
3938
|
+
for (const [{ message: { defaultMessage }, messageNode }] of msgs) {
|
|
3939
|
+
if (!defaultMessage || !messageNode) continue;
|
|
3940
|
+
let ast;
|
|
3941
|
+
try {
|
|
3942
|
+
ast = parse(defaultMessage, { ignoreTag: settings.ignoreTag });
|
|
3943
|
+
} catch (e) {
|
|
3944
|
+
context.report({
|
|
3945
|
+
node: messageNode,
|
|
3946
|
+
messageId: "parseError",
|
|
3947
|
+
data: { error: e.message }
|
|
3948
|
+
});
|
|
3949
|
+
continue;
|
|
3950
|
+
}
|
|
3951
|
+
const errors = verifyAst$3(ast);
|
|
3952
|
+
for (const error of errors) context.report({
|
|
3953
|
+
node,
|
|
3954
|
+
...error
|
|
3955
|
+
});
|
|
3956
|
+
}
|
|
3957
|
+
}
|
|
3958
|
+
const name$9 = "no-multiple-plurals";
|
|
3959
|
+
const rule$7 = {
|
|
3960
|
+
meta: {
|
|
3961
|
+
type: "problem",
|
|
3962
|
+
docs: {
|
|
3963
|
+
description: "Disallow multiple plural rules in the same message",
|
|
3964
|
+
url: "https://formatjs.github.io/docs/tooling/linter#no-multiple-plurals"
|
|
3965
|
+
},
|
|
3966
|
+
fixable: "code",
|
|
3967
|
+
schema: [],
|
|
3968
|
+
messages: {
|
|
3969
|
+
...CORE_MESSAGES,
|
|
3970
|
+
noMultiplePlurals: "Multiple plural rules in the same message"
|
|
3971
|
+
}
|
|
3972
|
+
},
|
|
3973
|
+
create(context) {
|
|
3974
|
+
const callExpressionVisitor = (node) => checkNode$5(context, node);
|
|
3975
|
+
const parserServices = context.sourceCode.parserServices;
|
|
3976
|
+
if (parserServices?.defineTemplateBodyVisitor) return parserServices.defineTemplateBodyVisitor({ CallExpression: callExpressionVisitor }, { CallExpression: callExpressionVisitor });
|
|
3977
|
+
return {
|
|
3978
|
+
JSXOpeningElement: (node) => checkNode$5(context, node),
|
|
3979
|
+
CallExpression: callExpressionVisitor
|
|
3980
|
+
};
|
|
3981
|
+
}
|
|
3982
|
+
};
|
|
3983
|
+
//#endregion
|
|
3984
|
+
//#region packages/eslint-plugin-formatjs/rules/no-multiple-whitespaces.ts
|
|
3985
|
+
function isAstValid(ast) {
|
|
3986
|
+
for (const element of ast) switch (element.type) {
|
|
3987
|
+
case TYPE.literal:
|
|
3988
|
+
if (/\s{2,}/gm.test(element.value)) return false;
|
|
3989
|
+
break;
|
|
3990
|
+
case TYPE.argument:
|
|
3991
|
+
case TYPE.date:
|
|
3992
|
+
case TYPE.number:
|
|
3993
|
+
case TYPE.pound:
|
|
3994
|
+
case TYPE.time: break;
|
|
3995
|
+
case TYPE.plural:
|
|
3996
|
+
case TYPE.select:
|
|
3997
|
+
for (const option of Object.values(element.options)) if (!isAstValid(option.value)) return false;
|
|
3998
|
+
break;
|
|
3999
|
+
case TYPE.tag: return isAstValid(element.children);
|
|
4000
|
+
}
|
|
4001
|
+
return true;
|
|
4002
|
+
}
|
|
4003
|
+
function trimMultiWhitespaces(message, ast) {
|
|
4004
|
+
const literalElements = [];
|
|
4005
|
+
const collectLiteralElements = (elements) => {
|
|
4006
|
+
for (const element of elements) switch (element.type) {
|
|
4007
|
+
case TYPE.literal:
|
|
4008
|
+
literalElements.push(element);
|
|
4009
|
+
break;
|
|
4010
|
+
case TYPE.argument:
|
|
4011
|
+
case TYPE.date:
|
|
4012
|
+
case TYPE.number:
|
|
4013
|
+
case TYPE.pound:
|
|
4014
|
+
case TYPE.time: break;
|
|
4015
|
+
case TYPE.plural:
|
|
4016
|
+
case TYPE.select:
|
|
4017
|
+
for (const option of Object.values(element.options)) collectLiteralElements(option.value);
|
|
4018
|
+
break;
|
|
4019
|
+
case TYPE.tag:
|
|
4020
|
+
collectLiteralElements(element.children);
|
|
4021
|
+
break;
|
|
4022
|
+
}
|
|
4023
|
+
};
|
|
4024
|
+
collectLiteralElements(ast);
|
|
4025
|
+
let trimmedFragments = [];
|
|
4026
|
+
let currentOffset = 0;
|
|
4027
|
+
for (const literal of literalElements) {
|
|
4028
|
+
const { start, end } = literal.location;
|
|
4029
|
+
const startOffset = start.offset;
|
|
4030
|
+
const endOffset = end.offset;
|
|
4031
|
+
trimmedFragments.push(message.slice(currentOffset, startOffset));
|
|
4032
|
+
trimmedFragments.push(message.slice(startOffset, endOffset).replace(/\s{2,}/gm, " "));
|
|
4033
|
+
currentOffset = endOffset;
|
|
4034
|
+
}
|
|
4035
|
+
trimmedFragments.push(message.slice(currentOffset));
|
|
4036
|
+
return trimmedFragments.join("");
|
|
4037
|
+
}
|
|
4038
|
+
function checkNode$4(context, node) {
|
|
4039
|
+
const msgs = extractMessages(node, getSettings(context));
|
|
4040
|
+
for (const [{ message: { defaultMessage }, messageNode }] of msgs) {
|
|
4041
|
+
if (!defaultMessage || !messageNode) continue;
|
|
4042
|
+
let ast;
|
|
4043
|
+
try {
|
|
4044
|
+
ast = parse(defaultMessage, { captureLocation: true });
|
|
4045
|
+
} catch (e) {
|
|
4046
|
+
context.report({
|
|
4047
|
+
node: messageNode,
|
|
4048
|
+
messageId: "parseError",
|
|
4049
|
+
data: { error: e instanceof Error ? e.message : String(e) }
|
|
4050
|
+
});
|
|
4051
|
+
return;
|
|
4052
|
+
}
|
|
4053
|
+
if (!isAstValid(ast)) {
|
|
4054
|
+
const newMessage = patchMessage(messageNode, ast, trimMultiWhitespaces);
|
|
4055
|
+
context.report({
|
|
4056
|
+
node: messageNode,
|
|
4057
|
+
messageId: "noMultipleWhitespaces",
|
|
4058
|
+
fix: newMessage !== null ? (fixer) => fixer.replaceText(messageNode, newMessage) : null
|
|
4059
|
+
});
|
|
4060
|
+
}
|
|
4061
|
+
}
|
|
4062
|
+
}
|
|
4063
|
+
const name$8 = "no-multiple-whitespaces";
|
|
4064
|
+
const rule$6 = {
|
|
4065
|
+
meta: {
|
|
4066
|
+
type: "problem",
|
|
4067
|
+
docs: {
|
|
4068
|
+
description: "Prevents usage of multiple consecutive whitespaces in message",
|
|
4069
|
+
url: "https://formatjs.github.io/docs/tooling/linter#no-multiple-whitespaces"
|
|
4070
|
+
},
|
|
4071
|
+
messages: {
|
|
4072
|
+
...CORE_MESSAGES,
|
|
4073
|
+
noMultipleWhitespaces: "Multiple consecutive whitespaces are not allowed"
|
|
4074
|
+
},
|
|
4075
|
+
fixable: "code",
|
|
4076
|
+
schema: []
|
|
4077
|
+
},
|
|
4078
|
+
create(context) {
|
|
4079
|
+
const callExpressionVisitor = (node) => checkNode$4(context, node);
|
|
4080
|
+
const parserServices = context.sourceCode.parserServices;
|
|
4081
|
+
if (parserServices?.defineTemplateBodyVisitor) return parserServices.defineTemplateBodyVisitor({ CallExpression: callExpressionVisitor }, { CallExpression: callExpressionVisitor });
|
|
4082
|
+
return {
|
|
4083
|
+
JSXOpeningElement: (node) => checkNode$4(context, node),
|
|
4084
|
+
CallExpression: callExpressionVisitor
|
|
4085
|
+
};
|
|
4086
|
+
}
|
|
4087
|
+
};
|
|
4088
|
+
//#endregion
|
|
4089
|
+
//#region packages/eslint-plugin-formatjs/rules/no-offset.ts
|
|
4090
|
+
function verifyAst$2(ast) {
|
|
4091
|
+
const errors = [];
|
|
4092
|
+
for (const el of ast) if (isPluralElement(el)) {
|
|
4093
|
+
if (el.offset) errors.push({
|
|
4094
|
+
messageId: "noOffset",
|
|
4095
|
+
data: {}
|
|
4096
|
+
});
|
|
4097
|
+
const { options } = el;
|
|
4098
|
+
for (const selector of Object.keys(options)) errors.push(...verifyAst$2(options[selector].value));
|
|
4099
|
+
}
|
|
4100
|
+
return errors;
|
|
4101
|
+
}
|
|
4102
|
+
function checkNode$3(context, node) {
|
|
4103
|
+
const settings = getSettings(context);
|
|
4104
|
+
const msgs = extractMessages(node, settings);
|
|
4105
|
+
for (const [{ message: { defaultMessage }, messageNode }] of msgs) {
|
|
4106
|
+
if (!defaultMessage || !messageNode) continue;
|
|
4107
|
+
let ast;
|
|
4108
|
+
try {
|
|
4109
|
+
ast = parse(defaultMessage, { ignoreTag: settings.ignoreTag });
|
|
4110
|
+
} catch (e) {
|
|
4111
|
+
context.report({
|
|
4112
|
+
node: messageNode,
|
|
4113
|
+
messageId: "parseError",
|
|
4114
|
+
data: { error: e.message }
|
|
4115
|
+
});
|
|
4116
|
+
continue;
|
|
4117
|
+
}
|
|
4118
|
+
const errors = verifyAst$2(ast);
|
|
4119
|
+
for (const error of errors) context.report({
|
|
4120
|
+
node: messageNode,
|
|
4121
|
+
...error
|
|
4122
|
+
});
|
|
4123
|
+
}
|
|
4124
|
+
}
|
|
4125
|
+
const name$7 = "no-offset";
|
|
4126
|
+
const rule$5 = {
|
|
4127
|
+
meta: {
|
|
4128
|
+
type: "problem",
|
|
4129
|
+
docs: {
|
|
4130
|
+
description: "Disallow offset in plural rules",
|
|
4131
|
+
url: "https://formatjs.github.io/docs/tooling/linter#no-offset"
|
|
4132
|
+
},
|
|
4133
|
+
fixable: "code",
|
|
4134
|
+
messages: {
|
|
4135
|
+
...CORE_MESSAGES,
|
|
4136
|
+
noOffset: "offset is not allowed"
|
|
4137
|
+
},
|
|
4138
|
+
schema: []
|
|
4139
|
+
},
|
|
4140
|
+
create(context) {
|
|
4141
|
+
const callExpressionVisitor = (node) => checkNode$3(context, node);
|
|
4142
|
+
const parserServices = context.sourceCode.parserServices;
|
|
4143
|
+
if (parserServices?.defineTemplateBodyVisitor) return parserServices.defineTemplateBodyVisitor({ CallExpression: callExpressionVisitor }, { CallExpression: callExpressionVisitor });
|
|
4144
|
+
return {
|
|
4145
|
+
JSXOpeningElement: (node) => checkNode$3(context, node),
|
|
4146
|
+
CallExpression: callExpressionVisitor
|
|
4147
|
+
};
|
|
4148
|
+
}
|
|
4149
|
+
};
|
|
4150
|
+
//#endregion
|
|
4151
|
+
//#region packages/eslint-plugin-formatjs/rules/no-useless-message.ts
|
|
4152
|
+
function verifyAst$1(ast) {
|
|
4153
|
+
if (ast.length !== 1) return;
|
|
4154
|
+
switch (ast[0].type) {
|
|
4155
|
+
case TYPE.argument: return "unnecessaryFormat";
|
|
4156
|
+
case TYPE.number: return "unnecessaryFormatNumber";
|
|
4157
|
+
case TYPE.date: return "unnecessaryFormatDate";
|
|
4158
|
+
case TYPE.time: return "unnecessaryFormatTime";
|
|
4159
|
+
}
|
|
4160
|
+
}
|
|
4161
|
+
function checkNode$2(context, node) {
|
|
4162
|
+
const settings = getSettings(context);
|
|
4163
|
+
const msgs = extractMessages(node, settings);
|
|
4164
|
+
for (const [{ message: { defaultMessage }, messageNode }] of msgs) {
|
|
4165
|
+
if (!defaultMessage || !messageNode) continue;
|
|
4166
|
+
let ast;
|
|
4167
|
+
try {
|
|
4168
|
+
ast = parse(defaultMessage, { ignoreTag: settings.ignoreTag });
|
|
4169
|
+
} catch (e) {
|
|
4170
|
+
context.report({
|
|
4171
|
+
node: messageNode,
|
|
4172
|
+
messageId: "parseError",
|
|
4173
|
+
data: { error: e.message }
|
|
4174
|
+
});
|
|
4175
|
+
continue;
|
|
4176
|
+
}
|
|
4177
|
+
const messageId = verifyAst$1(ast);
|
|
4178
|
+
if (messageId) context.report({
|
|
4179
|
+
node: messageNode,
|
|
4180
|
+
messageId
|
|
4181
|
+
});
|
|
4182
|
+
}
|
|
4183
|
+
}
|
|
4184
|
+
const name$6 = "no-useless-message";
|
|
4185
|
+
const rule$4 = {
|
|
4186
|
+
meta: {
|
|
4187
|
+
type: "problem",
|
|
4188
|
+
docs: {
|
|
4189
|
+
description: "Disallow unnecessary formatted message",
|
|
4190
|
+
url: "https://formatjs.github.io/docs/tooling/linter#no-useless-message"
|
|
4191
|
+
},
|
|
4192
|
+
fixable: "code",
|
|
4193
|
+
schema: [],
|
|
4194
|
+
messages: {
|
|
4195
|
+
...CORE_MESSAGES,
|
|
4196
|
+
unnecessaryFormat: "Unnecessary formatted message.",
|
|
4197
|
+
unnecessaryFormatNumber: "Unnecessary formatted message: just use FormattedNumber or intl.formatNumber.",
|
|
4198
|
+
unnecessaryFormatDate: "Unnecessary formatted message: just use FormattedDate or intl.formatDate.",
|
|
4199
|
+
unnecessaryFormatTime: "Unnecessary formatted message: just use FormattedTime or intl.formatTime."
|
|
4200
|
+
}
|
|
4201
|
+
},
|
|
4202
|
+
create(context) {
|
|
4203
|
+
const callExpressionVisitor = (node) => checkNode$2(context, node);
|
|
4204
|
+
const parserServices = context.sourceCode.parserServices;
|
|
4205
|
+
if (parserServices?.defineTemplateBodyVisitor) return parserServices.defineTemplateBodyVisitor({ CallExpression: callExpressionVisitor }, { CallExpression: callExpressionVisitor });
|
|
4206
|
+
return {
|
|
4207
|
+
JSXOpeningElement: (node) => checkNode$2(context, node),
|
|
4208
|
+
CallExpression: callExpressionVisitor
|
|
4209
|
+
};
|
|
4210
|
+
}
|
|
4211
|
+
};
|
|
4212
|
+
//#endregion
|
|
4213
|
+
//#region packages/eslint-plugin-formatjs/rules/prefer-formatted-message.ts
|
|
4214
|
+
const name$5 = "prefer-formatted-message";
|
|
4215
|
+
const rule$3 = {
|
|
4216
|
+
meta: {
|
|
4217
|
+
type: "suggestion",
|
|
4218
|
+
docs: {
|
|
4219
|
+
description: "Prefer `FormattedMessage` component over `intl.formatMessage` if applicable.",
|
|
4220
|
+
url: "https://formatjs.github.io/docs/tooling/linter#prefer-formatted-message"
|
|
4221
|
+
},
|
|
4222
|
+
messages: { jsxChildren: "Prefer `FormattedMessage` over `intl.formatMessage` in the JSX children expression." },
|
|
4223
|
+
schema: []
|
|
4224
|
+
},
|
|
4225
|
+
create(context) {
|
|
4226
|
+
return { JSXElement: (node) => {
|
|
4227
|
+
node.children.forEach((child) => {
|
|
4228
|
+
if (child.type !== "JSXExpressionContainer" || !isIntlFormatMessageCall(child.expression)) return;
|
|
4229
|
+
context.report({
|
|
4230
|
+
node: child,
|
|
4231
|
+
messageId: "jsxChildren"
|
|
4232
|
+
});
|
|
4233
|
+
});
|
|
4234
|
+
} };
|
|
4235
|
+
}
|
|
4236
|
+
};
|
|
4237
|
+
//#endregion
|
|
4238
|
+
//#region packages/eslint-plugin-formatjs/rules/prefer-pound-in-plural.ts
|
|
4239
|
+
function verifyAst(context, messageNode, ast) {
|
|
4240
|
+
const patches = [];
|
|
4241
|
+
_verifyAst(ast);
|
|
4242
|
+
if (patches.length > 0) {
|
|
4243
|
+
const patchedMessage = patchMessage(messageNode, ast, (content) => {
|
|
4244
|
+
return patches.reduce((magicString, patch) => {
|
|
4245
|
+
switch (patch.type) {
|
|
4246
|
+
case "prependLeft": return magicString.prependLeft(patch.index, patch.content);
|
|
4247
|
+
case "remove": return magicString.remove(patch.start, patch.end);
|
|
4248
|
+
case "update": return magicString.update(patch.start, patch.end, patch.content);
|
|
4249
|
+
}
|
|
4250
|
+
}, new MagicString(content)).toString();
|
|
4251
|
+
});
|
|
4252
|
+
context.report({
|
|
4253
|
+
node: messageNode,
|
|
4254
|
+
messageId: "preferPoundInPlurals",
|
|
4255
|
+
fix: patchedMessage !== null ? (fixer) => fixer.replaceText(messageNode, patchedMessage) : null
|
|
4256
|
+
});
|
|
4257
|
+
}
|
|
4258
|
+
function _verifyAst(ast) {
|
|
4259
|
+
for (let i = 0; i < ast.length; i++) {
|
|
4260
|
+
const current = ast[i];
|
|
4261
|
+
switch (current.type) {
|
|
4262
|
+
case TYPE.argument:
|
|
4263
|
+
case TYPE.number: {
|
|
4264
|
+
if (current.type === TYPE.number && current.style) break;
|
|
4265
|
+
const next = ast[i + 1];
|
|
4266
|
+
const nextNext = ast[i + 2];
|
|
4267
|
+
if (next && nextNext && next.type === TYPE.literal && next.value === " " && nextNext.type === TYPE.plural && nextNext.value === current.value) _removeRangeAndPrependPluralClauses(current.location.start.offset, next.location.end.offset, nextNext, "# ");
|
|
4268
|
+
else if (next && next.type === TYPE.plural && next.value === current.value) _removeRangeAndPrependPluralClauses(current.location.start.offset, current.location.end.offset, next, "#");
|
|
4269
|
+
break;
|
|
4270
|
+
}
|
|
4271
|
+
case TYPE.plural: {
|
|
4272
|
+
const name = current.value;
|
|
4273
|
+
for (const { value } of Object.values(current.options)) _replacementArgumentWithPound(name, value);
|
|
4274
|
+
break;
|
|
4275
|
+
}
|
|
4276
|
+
case TYPE.select:
|
|
4277
|
+
for (const { value } of Object.values(current.options)) _verifyAst(value);
|
|
4278
|
+
break;
|
|
4279
|
+
case TYPE.tag:
|
|
4280
|
+
_verifyAst(current.children);
|
|
4281
|
+
break;
|
|
4282
|
+
default: break;
|
|
4283
|
+
}
|
|
4284
|
+
}
|
|
4285
|
+
}
|
|
4286
|
+
function _replacementArgumentWithPound(name, ast) {
|
|
4287
|
+
for (const element of ast) switch (element.type) {
|
|
4288
|
+
case TYPE.argument:
|
|
4289
|
+
case TYPE.number:
|
|
4290
|
+
if (element.value === name && (element.type !== TYPE.number || !element.style)) patches.push({
|
|
4291
|
+
type: "update",
|
|
4292
|
+
start: element.location.start.offset,
|
|
4293
|
+
end: element.location.end.offset,
|
|
4294
|
+
content: "#"
|
|
4295
|
+
});
|
|
4296
|
+
break;
|
|
4297
|
+
case TYPE.tag:
|
|
4298
|
+
_replacementArgumentWithPound(name, element.children);
|
|
4299
|
+
break;
|
|
4300
|
+
case TYPE.select:
|
|
4301
|
+
for (const { value } of Object.values(element.options)) _replacementArgumentWithPound(name, value);
|
|
4302
|
+
break;
|
|
4303
|
+
default: break;
|
|
4304
|
+
}
|
|
4305
|
+
}
|
|
4306
|
+
function _removeRangeAndPrependPluralClauses(rangeToRemoveStart, rangeToRemoveEnd, pluralElement, prependText) {
|
|
4307
|
+
patches.push({
|
|
4308
|
+
type: "remove",
|
|
4309
|
+
start: rangeToRemoveStart,
|
|
4310
|
+
end: rangeToRemoveEnd
|
|
4311
|
+
});
|
|
4312
|
+
for (const { location } of Object.values(pluralElement.options)) patches.push({
|
|
4313
|
+
type: "prependLeft",
|
|
4314
|
+
index: location.start.offset + 1,
|
|
4315
|
+
content: prependText
|
|
4316
|
+
});
|
|
4317
|
+
}
|
|
4318
|
+
}
|
|
4319
|
+
function checkNode$1(context, node) {
|
|
4320
|
+
const msgs = extractMessages(node, getSettings(context));
|
|
4321
|
+
for (const [{ message: { defaultMessage }, messageNode }] of msgs) {
|
|
4322
|
+
if (!defaultMessage || !messageNode) continue;
|
|
4323
|
+
let ast;
|
|
4324
|
+
try {
|
|
4325
|
+
ast = parse(defaultMessage, { captureLocation: true });
|
|
4326
|
+
} catch (e) {
|
|
4327
|
+
context.report({
|
|
4328
|
+
node: messageNode,
|
|
4329
|
+
messageId: "parseError",
|
|
4330
|
+
data: { error: e instanceof Error ? e.message : String(e) }
|
|
4331
|
+
});
|
|
4332
|
+
return;
|
|
4333
|
+
}
|
|
4334
|
+
verifyAst(context, messageNode, ast);
|
|
4335
|
+
}
|
|
4336
|
+
}
|
|
4337
|
+
const name$4 = "prefer-pound-in-plural";
|
|
4338
|
+
const rule$2 = {
|
|
4339
|
+
meta: {
|
|
4340
|
+
type: "suggestion",
|
|
4341
|
+
docs: {
|
|
4342
|
+
description: "Prefer using # to reference the count in the plural argument.",
|
|
4343
|
+
url: "https://formatjs.github.io/docs/tooling/linter#prefer-pound-in-plurals"
|
|
4344
|
+
},
|
|
4345
|
+
messages: {
|
|
4346
|
+
...CORE_MESSAGES,
|
|
4347
|
+
preferPoundInPlurals: "Prefer using # to reference the count in the plural argument instead of repeating the argument."
|
|
4348
|
+
},
|
|
4349
|
+
fixable: "code",
|
|
4350
|
+
schema: []
|
|
4351
|
+
},
|
|
4352
|
+
create(context) {
|
|
4353
|
+
const callExpressionVisitor = (node) => checkNode$1(context, node);
|
|
4354
|
+
const parserServices = context.sourceCode.parserServices;
|
|
4355
|
+
if (parserServices?.defineTemplateBodyVisitor) return parserServices.defineTemplateBodyVisitor({ CallExpression: callExpressionVisitor }, { CallExpression: callExpressionVisitor });
|
|
4356
|
+
return {
|
|
4357
|
+
JSXOpeningElement: (node) => checkNode$1(context, node),
|
|
4358
|
+
CallExpression: callExpressionVisitor
|
|
4359
|
+
};
|
|
4360
|
+
}
|
|
4361
|
+
};
|
|
4362
|
+
//#endregion
|
|
4363
|
+
//#region packages/eslint-plugin-formatjs/rules/no-literal-string-in-object.ts
|
|
4364
|
+
const name$3 = "no-literal-string-in-object";
|
|
4365
|
+
const rule$1 = {
|
|
4366
|
+
meta: {
|
|
4367
|
+
type: "problem",
|
|
4368
|
+
docs: {
|
|
4369
|
+
description: "Enforce translation of specific object properties",
|
|
4370
|
+
url: "https://formatjs.github.io/docs/tooling/linter#no-literal-string-in-object"
|
|
4371
|
+
},
|
|
4372
|
+
schema: [{
|
|
4373
|
+
type: "object",
|
|
4374
|
+
properties: { include: {
|
|
4375
|
+
type: "array",
|
|
4376
|
+
items: { type: "string" },
|
|
4377
|
+
default: ["label"]
|
|
4378
|
+
} },
|
|
4379
|
+
additionalProperties: false
|
|
4380
|
+
}],
|
|
4381
|
+
messages: { untranslatedProperty: "Object property: `{{propertyKey}}` might contain an untranslated literal string" }
|
|
4382
|
+
},
|
|
4383
|
+
create(context) {
|
|
4384
|
+
const propertyVisitor = (node) => {
|
|
4385
|
+
checkProperty(context, node);
|
|
4386
|
+
};
|
|
4387
|
+
const parserServices = context.sourceCode.parserServices;
|
|
4388
|
+
if (parserServices?.defineTemplateBodyVisitor) return parserServices.defineTemplateBodyVisitor({ Property: propertyVisitor }, { Property: propertyVisitor });
|
|
4389
|
+
return { Property: propertyVisitor };
|
|
4390
|
+
}
|
|
4391
|
+
};
|
|
4392
|
+
function checkProperty(context, node) {
|
|
4393
|
+
const config = {
|
|
4394
|
+
include: ["label"],
|
|
4395
|
+
...context.options[0]
|
|
4396
|
+
};
|
|
4397
|
+
const propertyKey = node.key.type === "Identifier" ? node.key.name : node.key.type === "Literal" && typeof node.key.value === "string" ? node.key.value : null;
|
|
4398
|
+
if (!propertyKey || !config.include.includes(propertyKey)) return;
|
|
4399
|
+
checkPropertyValue(context, node.value, propertyKey);
|
|
4400
|
+
}
|
|
4401
|
+
function checkPropertyValue(context, node, propertyKey) {
|
|
4402
|
+
if (node.type === "Literal" && typeof node.value === "string" && node.value.length > 0 || node.type === "TemplateLiteral" && (node.quasis.length > 1 || node.quasis[0].value.raw.length > 0)) context.report({
|
|
4403
|
+
node,
|
|
4404
|
+
messageId: "untranslatedProperty",
|
|
4405
|
+
data: { propertyKey }
|
|
4406
|
+
});
|
|
4407
|
+
else if (node.type === "BinaryExpression" && node.operator === "+") {
|
|
4408
|
+
checkPropertyValue(context, node.left, propertyKey);
|
|
4409
|
+
checkPropertyValue(context, node.right, propertyKey);
|
|
4410
|
+
} else if (node.type === "ConditionalExpression") {
|
|
4411
|
+
checkPropertyValue(context, node.consequent, propertyKey);
|
|
4412
|
+
checkPropertyValue(context, node.alternate, propertyKey);
|
|
4413
|
+
} else if (node.type === "LogicalExpression") {
|
|
4414
|
+
checkPropertyValue(context, node.left, propertyKey);
|
|
4415
|
+
checkPropertyValue(context, node.right, propertyKey);
|
|
4416
|
+
}
|
|
4417
|
+
}
|
|
4418
|
+
//#endregion
|
|
4419
|
+
//#region packages/eslint-plugin-formatjs/rules/prefer-full-sentence.ts
|
|
4420
|
+
/**
|
|
4421
|
+
* Get the first boundary element, recursing into tags since
|
|
4422
|
+
* <b>hello</b> starts with the literal "hello".
|
|
4423
|
+
*/
|
|
4424
|
+
function getFirstBoundaryElement(ast) {
|
|
4425
|
+
if (ast.length === 0) return null;
|
|
4426
|
+
const first = ast[0];
|
|
4427
|
+
if (first.type === TYPE.tag) return getFirstBoundaryElement(first.children);
|
|
4428
|
+
return first;
|
|
4429
|
+
}
|
|
4430
|
+
/**
|
|
4431
|
+
* Get the last boundary element, recursing into tags.
|
|
4432
|
+
*/
|
|
4433
|
+
function getLastBoundaryElement(ast) {
|
|
4434
|
+
if (ast.length === 0) return null;
|
|
4435
|
+
const last = ast[ast.length - 1];
|
|
4436
|
+
if (last.type === TYPE.tag) return getLastBoundaryElement(last.children);
|
|
4437
|
+
return last;
|
|
4438
|
+
}
|
|
4439
|
+
function findWhitespaceIssues(ast) {
|
|
4440
|
+
const issues = [];
|
|
4441
|
+
const first = getFirstBoundaryElement(ast);
|
|
4442
|
+
const last = getLastBoundaryElement(ast);
|
|
4443
|
+
if (first === last && first?.type === TYPE.literal && ast.length === 1 && first.value.trim() === "") return issues;
|
|
4444
|
+
if (first?.type === TYPE.literal && /^\s/.test(first.value)) issues.push("leadingWhitespace");
|
|
4445
|
+
if (last?.type === TYPE.literal && /\s$/.test(last.value)) issues.push("trailingWhitespace");
|
|
4446
|
+
for (const element of ast) switch (element.type) {
|
|
4447
|
+
case TYPE.plural:
|
|
4448
|
+
case TYPE.select:
|
|
4449
|
+
for (const option of Object.values(element.options)) issues.push(...findWhitespaceIssues(option.value));
|
|
4450
|
+
break;
|
|
4451
|
+
}
|
|
4452
|
+
return issues;
|
|
4453
|
+
}
|
|
4454
|
+
function checkNode(context, node) {
|
|
4455
|
+
const msgs = extractMessages(node, getSettings(context));
|
|
4456
|
+
for (const [{ message: { defaultMessage }, messageNode }] of msgs) {
|
|
4457
|
+
if (!defaultMessage || !messageNode) continue;
|
|
4458
|
+
let ast;
|
|
4459
|
+
try {
|
|
4460
|
+
ast = parse(defaultMessage);
|
|
4461
|
+
} catch (e) {
|
|
4462
|
+
context.report({
|
|
4463
|
+
node: messageNode,
|
|
4464
|
+
messageId: "parseError",
|
|
4465
|
+
data: { error: e instanceof Error ? e.message : String(e) }
|
|
4466
|
+
});
|
|
4467
|
+
return;
|
|
4468
|
+
}
|
|
4469
|
+
const issues = findWhitespaceIssues(ast);
|
|
4470
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4471
|
+
for (const issue of issues) {
|
|
4472
|
+
if (seen.has(issue)) continue;
|
|
4473
|
+
seen.add(issue);
|
|
4474
|
+
context.report({
|
|
4475
|
+
node: messageNode,
|
|
4476
|
+
messageId: issue
|
|
4477
|
+
});
|
|
4478
|
+
}
|
|
4479
|
+
}
|
|
4480
|
+
}
|
|
4481
|
+
const name$2 = "prefer-full-sentence";
|
|
4482
|
+
const rule = {
|
|
4483
|
+
meta: {
|
|
4484
|
+
type: "suggestion",
|
|
4485
|
+
docs: {
|
|
4486
|
+
description: "Detects messages with leading/trailing whitespace, which suggests string concatenation instead of full sentences",
|
|
4487
|
+
url: "https://formatjs.github.io/docs/tooling/linter#prefer-full-sentence"
|
|
4488
|
+
},
|
|
4489
|
+
messages: {
|
|
4490
|
+
...CORE_MESSAGES,
|
|
4491
|
+
leadingWhitespace: "Messages should be full sentences — leading whitespace suggests string concatenation",
|
|
4492
|
+
trailingWhitespace: "Messages should be full sentences — trailing whitespace suggests string concatenation"
|
|
4493
|
+
},
|
|
4494
|
+
schema: []
|
|
4495
|
+
},
|
|
4496
|
+
create(context) {
|
|
4497
|
+
const callExpressionVisitor = (node) => checkNode(context, node);
|
|
4498
|
+
const parserServices = context.sourceCode.parserServices;
|
|
4499
|
+
if (parserServices?.defineTemplateBodyVisitor) return parserServices.defineTemplateBodyVisitor({ CallExpression: callExpressionVisitor }, { CallExpression: callExpressionVisitor });
|
|
4500
|
+
return {
|
|
4501
|
+
JSXOpeningElement: (node) => checkNode(context, node),
|
|
4502
|
+
CallExpression: callExpressionVisitor
|
|
4503
|
+
};
|
|
4504
|
+
}
|
|
4505
|
+
};
|
|
4506
|
+
//#endregion
|
|
4507
|
+
//#region packages/eslint-plugin-formatjs/package.json
|
|
4508
|
+
var package_exports = /* @__PURE__ */ __exportAll({
|
|
4509
|
+
author: () => author,
|
|
4510
|
+
bugs: () => bugs,
|
|
4511
|
+
default: () => package_default,
|
|
4512
|
+
dependencies: () => dependencies,
|
|
4513
|
+
description: () => description,
|
|
4514
|
+
exports: () => exports,
|
|
4515
|
+
homepage: () => homepage,
|
|
4516
|
+
keywords: () => keywords,
|
|
4517
|
+
license: () => license,
|
|
4518
|
+
main: () => main,
|
|
4519
|
+
name: () => name$1,
|
|
4520
|
+
peerDependencies: () => peerDependencies,
|
|
4521
|
+
repository: () => repository,
|
|
4522
|
+
type: () => type,
|
|
4523
|
+
types: () => types,
|
|
4524
|
+
version: () => version$1
|
|
4525
|
+
});
|
|
4526
|
+
var name$1 = "eslint-plugin-formatjs";
|
|
4527
|
+
var description = "ESLint plugin for formatjs";
|
|
4528
|
+
var version$1 = "6.4.5";
|
|
4529
|
+
var license = "MIT";
|
|
4530
|
+
var author = "Long Ho <holevietlong@gmail.com>";
|
|
4531
|
+
var type = "module";
|
|
4532
|
+
var types = "index.d.ts";
|
|
4533
|
+
var exports = {
|
|
4534
|
+
".": "./index.js",
|
|
4535
|
+
"./util": "./util.js"
|
|
4536
|
+
};
|
|
4537
|
+
var dependencies = {
|
|
4538
|
+
"@formatjs/icu-messageformat-parser": "workspace:*",
|
|
4539
|
+
"@formatjs/ts-transformer": "workspace:*",
|
|
4540
|
+
"@types/picomatch": "^4.0.0",
|
|
4541
|
+
"@unicode/unicode-17.0.0": "^1.6.16",
|
|
4542
|
+
"magic-string": "^0.30.0",
|
|
4543
|
+
"picomatch": "2 || 3 || 4"
|
|
4544
|
+
};
|
|
4545
|
+
var peerDependencies = { "eslint": "9 || 10" };
|
|
4546
|
+
var bugs = "https://github.com/formatjs/formatjs/issues";
|
|
4547
|
+
var homepage = "https://github.com/formatjs/formatjs#readme";
|
|
4548
|
+
var keywords = [
|
|
4549
|
+
"eslint",
|
|
4550
|
+
"eslintplugin",
|
|
4551
|
+
"formatjs",
|
|
4552
|
+
"i18n"
|
|
4553
|
+
];
|
|
4554
|
+
var main = "index.js";
|
|
4555
|
+
var repository = "formatjs/formatjs.git";
|
|
4556
|
+
var package_default = {
|
|
4557
|
+
name: name$1,
|
|
4558
|
+
description,
|
|
4559
|
+
version: version$1,
|
|
4560
|
+
license,
|
|
4561
|
+
author,
|
|
4562
|
+
type,
|
|
4563
|
+
types,
|
|
4564
|
+
exports,
|
|
4565
|
+
dependencies,
|
|
4566
|
+
peerDependencies,
|
|
4567
|
+
bugs,
|
|
4568
|
+
homepage,
|
|
4569
|
+
keywords,
|
|
4570
|
+
main,
|
|
4571
|
+
repository
|
|
4572
|
+
};
|
|
4573
|
+
//#endregion
|
|
4574
|
+
//#region packages/eslint-plugin-formatjs/index.ts
|
|
4575
|
+
const { name, version } = package_default ?? package_exports;
|
|
50
4576
|
const plugin = {
|
|
51
4577
|
meta: {
|
|
52
4578
|
name,
|
|
53
4579
|
version
|
|
54
4580
|
},
|
|
55
|
-
rules
|
|
4581
|
+
rules: {
|
|
4582
|
+
[name$22]: rule$20,
|
|
4583
|
+
[name$21]: rule$19,
|
|
4584
|
+
[name$20]: rule$18,
|
|
4585
|
+
[name$19]: rule$17,
|
|
4586
|
+
[name$18]: rule$16,
|
|
4587
|
+
[name$17]: rule$15,
|
|
4588
|
+
[name$16]: rule$14,
|
|
4589
|
+
[name$15]: rule$13,
|
|
4590
|
+
[name$14]: rule$12,
|
|
4591
|
+
[name$13]: rule$11,
|
|
4592
|
+
[name$12]: rule$10,
|
|
4593
|
+
[name$11]: rule$9,
|
|
4594
|
+
[name$9]: rule$7,
|
|
4595
|
+
[name$8]: rule$6,
|
|
4596
|
+
[name$7]: rule$5,
|
|
4597
|
+
[name$6]: rule$4,
|
|
4598
|
+
[name$5]: rule$3,
|
|
4599
|
+
[name$4]: rule$2,
|
|
4600
|
+
[name$10]: rule$8,
|
|
4601
|
+
[name$3]: rule$1,
|
|
4602
|
+
[name$2]: rule
|
|
4603
|
+
},
|
|
56
4604
|
configs: {}
|
|
57
4605
|
};
|
|
58
|
-
|
|
59
|
-
const configs = {
|
|
4606
|
+
plugin.configs = {
|
|
60
4607
|
strict: {
|
|
61
4608
|
name: "formatjs/strict",
|
|
62
4609
|
plugins: { formatjs: plugin },
|
|
@@ -107,5 +4654,7 @@ const configs = {
|
|
|
107
4654
|
}
|
|
108
4655
|
}
|
|
109
4656
|
};
|
|
110
|
-
|
|
111
|
-
export default
|
|
4657
|
+
//#endregion
|
|
4658
|
+
export { plugin as default };
|
|
4659
|
+
|
|
4660
|
+
//# sourceMappingURL=index.js.map
|