eslint-plugin-formatjs 6.4.4 → 6.4.6
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/util.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
//#region packages/eslint-plugin-formatjs/util.ts
|
|
1
2
|
const FORMAT_FUNCTION_NAMES = new Set([
|
|
2
3
|
"$formatMessage",
|
|
3
4
|
"formatMessage",
|
|
@@ -5,7 +6,7 @@ const FORMAT_FUNCTION_NAMES = new Set([
|
|
|
5
6
|
]);
|
|
6
7
|
const COMPONENT_NAMES = new Set(["FormattedMessage"]);
|
|
7
8
|
const DECLARATION_FUNCTION_NAMES = new Set(["defineMessage"]);
|
|
8
|
-
|
|
9
|
+
function getSettings({ settings }) {
|
|
9
10
|
return settings.formatjs ?? settings;
|
|
10
11
|
}
|
|
11
12
|
function isStringLiteral(node) {
|
|
@@ -17,35 +18,19 @@ function isTemplateLiteralWithoutVar(node) {
|
|
|
17
18
|
function staticallyEvaluateStringConcat(node) {
|
|
18
19
|
const right = node.right;
|
|
19
20
|
const left = node.left;
|
|
20
|
-
if (!isStringLiteral(right))
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
if (isStringLiteral(left)) {
|
|
24
|
-
return [left.value + right.value, true];
|
|
25
|
-
}
|
|
21
|
+
if (!isStringLiteral(right)) return ["", false];
|
|
22
|
+
if (isStringLiteral(left)) return [left.value + right.value, true];
|
|
26
23
|
if (node.left.type === "BinaryExpression") {
|
|
27
24
|
const [result, isStaticallyEvaluatable] = staticallyEvaluateStringConcat(node.left);
|
|
28
25
|
return [result + right.value, isStaticallyEvaluatable];
|
|
29
26
|
}
|
|
30
27
|
return ["", false];
|
|
31
28
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (node.type !== "
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
// Check if call has at least one argument that is an object expression
|
|
38
|
-
if (node.arguments.length < 1 || node.arguments[0].type !== "ObjectExpression") {
|
|
39
|
-
return false;
|
|
40
|
-
}
|
|
41
|
-
// Pattern 1: intl.formatMessage() or something.intl.formatMessage()
|
|
42
|
-
if (node.callee.type === "MemberExpression") {
|
|
43
|
-
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");
|
|
44
|
-
}
|
|
45
|
-
// Pattern 2: formatMessage() (destructured from useIntl)
|
|
46
|
-
if (node.callee.type === "Identifier") {
|
|
47
|
-
return FORMAT_FUNCTION_NAMES.has(node.callee.name);
|
|
48
|
-
}
|
|
29
|
+
function isIntlFormatMessageCall(node) {
|
|
30
|
+
if (node.type !== "CallExpression") return false;
|
|
31
|
+
if (node.arguments.length < 1 || node.arguments[0].type !== "ObjectExpression") return false;
|
|
32
|
+
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");
|
|
33
|
+
if (node.callee.type === "Identifier") return FORMAT_FUNCTION_NAMES.has(node.callee.name);
|
|
49
34
|
return false;
|
|
50
35
|
}
|
|
51
36
|
function isSingleMessageDescriptorDeclaration(node, functionNames) {
|
|
@@ -54,45 +39,31 @@ function isSingleMessageDescriptorDeclaration(node, functionNames) {
|
|
|
54
39
|
function isMultipleMessageDescriptorDeclaration(node) {
|
|
55
40
|
return node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "defineMessages";
|
|
56
41
|
}
|
|
57
|
-
|
|
58
|
-
if (!node || node.type !== "ObjectExpression")
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
42
|
+
function extractMessageDescriptor(node) {
|
|
43
|
+
if (!node || node.type !== "ObjectExpression") return;
|
|
61
44
|
const result = {
|
|
62
45
|
messageDescriptorNode: node,
|
|
63
46
|
message: {},
|
|
64
|
-
messageNode:
|
|
65
|
-
messagePropNode:
|
|
66
|
-
descriptionNode:
|
|
67
|
-
idValueNode:
|
|
47
|
+
messageNode: void 0,
|
|
48
|
+
messagePropNode: void 0,
|
|
49
|
+
descriptionNode: void 0,
|
|
50
|
+
idValueNode: void 0
|
|
68
51
|
};
|
|
69
52
|
for (const prop of node.properties) {
|
|
70
|
-
if (prop.type !== "Property" || prop.key.type !== "Identifier")
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
// Only extract values for message-related props
|
|
74
|
-
// GH #5069: Don't process other props like tagName, values, etc.
|
|
53
|
+
if (prop.type !== "Property" || prop.key.type !== "Identifier") continue;
|
|
75
54
|
const propName = prop.key.name;
|
|
76
|
-
if (propName !== "id" && propName !== "defaultMessage" && propName !== "description")
|
|
77
|
-
continue;
|
|
78
|
-
}
|
|
55
|
+
if (propName !== "id" && propName !== "defaultMessage" && propName !== "description") continue;
|
|
79
56
|
const valueNode = prop.value;
|
|
80
|
-
let value =
|
|
81
|
-
if (isStringLiteral(valueNode))
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
value = valueNode.quasis[0].value.cooked ?? undefined;
|
|
85
|
-
} else if (valueNode.type === "TaggedTemplateExpression") {
|
|
57
|
+
let value = void 0;
|
|
58
|
+
if (isStringLiteral(valueNode)) value = valueNode.value;
|
|
59
|
+
else if (isTemplateLiteralWithoutVar(valueNode)) value = valueNode.quasis[0].value.cooked ?? void 0;
|
|
60
|
+
else if (valueNode.type === "TaggedTemplateExpression") {
|
|
86
61
|
const { quasi } = valueNode;
|
|
87
|
-
if (!isTemplateLiteralWithoutVar(quasi))
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
value = quasi.quasis[0].value.cooked ?? undefined;
|
|
62
|
+
if (!isTemplateLiteralWithoutVar(quasi)) throw new Error("Tagged template expression must be no substitution");
|
|
63
|
+
value = quasi.quasis[0].value.cooked ?? void 0;
|
|
91
64
|
} else if (valueNode.type === "BinaryExpression") {
|
|
92
65
|
const [result, isStatic] = staticallyEvaluateStringConcat(valueNode);
|
|
93
|
-
if (isStatic)
|
|
94
|
-
value = result;
|
|
95
|
-
}
|
|
66
|
+
if (isStatic) value = result;
|
|
96
67
|
}
|
|
97
68
|
switch (propName) {
|
|
98
69
|
case "defaultMessage":
|
|
@@ -114,54 +85,37 @@ export function extractMessageDescriptor(node) {
|
|
|
114
85
|
return result;
|
|
115
86
|
}
|
|
116
87
|
function extractMessageDescriptorFromJSXElement(node) {
|
|
117
|
-
if (!node || !node.attributes)
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
88
|
+
if (!node || !node.attributes) return;
|
|
120
89
|
let values;
|
|
121
90
|
const result = {
|
|
122
91
|
messageDescriptorNode: node,
|
|
123
92
|
message: {},
|
|
124
|
-
messageNode:
|
|
125
|
-
messagePropNode:
|
|
126
|
-
descriptionNode:
|
|
127
|
-
idValueNode:
|
|
128
|
-
idPropNode:
|
|
93
|
+
messageNode: void 0,
|
|
94
|
+
messagePropNode: void 0,
|
|
95
|
+
descriptionNode: void 0,
|
|
96
|
+
idValueNode: void 0,
|
|
97
|
+
idPropNode: void 0
|
|
129
98
|
};
|
|
130
99
|
let hasSpreadAttribute = false;
|
|
131
100
|
for (const prop of node.attributes) {
|
|
132
|
-
|
|
133
|
-
if (prop.type
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
if (prop.type !== "JSXAttribute" || prop.name.type !== "JSXIdentifier") {
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
const key = prop.name;
|
|
140
|
-
const keyName = key.name;
|
|
141
|
-
// Only extract values for message-related props
|
|
142
|
-
// GH #5069: Don't process other props like tagName, values, etc.
|
|
143
|
-
// Allow them to have tagged templates with substitutions
|
|
101
|
+
if (prop.type === "JSXSpreadAttribute") hasSpreadAttribute = true;
|
|
102
|
+
if (prop.type !== "JSXAttribute" || prop.name.type !== "JSXIdentifier") continue;
|
|
103
|
+
const keyName = prop.name.name;
|
|
144
104
|
const isMessageProp = keyName === "id" || keyName === "defaultMessage" || keyName === "description";
|
|
145
105
|
let valueNode = prop.value;
|
|
146
|
-
let value =
|
|
106
|
+
let value = void 0;
|
|
147
107
|
if (valueNode && isMessageProp) {
|
|
148
|
-
if (isStringLiteral(valueNode))
|
|
149
|
-
|
|
150
|
-
} else if (valueNode?.type === "JSXExpressionContainer") {
|
|
108
|
+
if (isStringLiteral(valueNode)) value = valueNode.value;
|
|
109
|
+
else if (valueNode?.type === "JSXExpressionContainer") {
|
|
151
110
|
const { expression } = valueNode;
|
|
152
111
|
if (expression.type === "BinaryExpression") {
|
|
153
112
|
const [result, isStatic] = staticallyEvaluateStringConcat(expression);
|
|
154
|
-
if (isStatic)
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
} else if (isTemplateLiteralWithoutVar(expression)) {
|
|
158
|
-
value = expression.quasis[0].value.cooked ?? undefined;
|
|
159
|
-
} else if (expression.type === "TaggedTemplateExpression") {
|
|
113
|
+
if (isStatic) value = result;
|
|
114
|
+
} else if (isTemplateLiteralWithoutVar(expression)) value = expression.quasis[0].value.cooked ?? void 0;
|
|
115
|
+
else if (expression.type === "TaggedTemplateExpression") {
|
|
160
116
|
const { quasi } = expression;
|
|
161
|
-
if (!isTemplateLiteralWithoutVar(quasi))
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
value = quasi.quasis[0].value.cooked ?? undefined;
|
|
117
|
+
if (!isTemplateLiteralWithoutVar(quasi)) throw new Error("Tagged template expression must be no substitution");
|
|
118
|
+
value = quasi.quasis[0].value.cooked ?? void 0;
|
|
165
119
|
}
|
|
166
120
|
}
|
|
167
121
|
}
|
|
@@ -169,78 +123,51 @@ function extractMessageDescriptorFromJSXElement(node) {
|
|
|
169
123
|
case "defaultMessage":
|
|
170
124
|
result.messagePropNode = prop;
|
|
171
125
|
result.messageNode = valueNode;
|
|
172
|
-
if (value)
|
|
173
|
-
result.message.defaultMessage = value;
|
|
174
|
-
}
|
|
126
|
+
if (value) result.message.defaultMessage = value;
|
|
175
127
|
break;
|
|
176
128
|
case "description":
|
|
177
129
|
result.descriptionNode = valueNode;
|
|
178
|
-
if (value)
|
|
179
|
-
result.message.description = value;
|
|
180
|
-
}
|
|
130
|
+
if (value) result.message.description = value;
|
|
181
131
|
break;
|
|
182
132
|
case "id":
|
|
183
133
|
result.idValueNode = valueNode;
|
|
184
134
|
result.idPropNode = prop;
|
|
185
|
-
if (value)
|
|
186
|
-
result.message.id = value;
|
|
187
|
-
}
|
|
135
|
+
if (value) result.message.id = value;
|
|
188
136
|
break;
|
|
189
137
|
case "values":
|
|
190
|
-
if (valueNode?.type === "JSXExpressionContainer" && valueNode.expression.type === "ObjectExpression")
|
|
191
|
-
values = valueNode.expression;
|
|
192
|
-
}
|
|
138
|
+
if (valueNode?.type === "JSXExpressionContainer" && valueNode.expression.type === "ObjectExpression") values = valueNode.expression;
|
|
193
139
|
break;
|
|
194
140
|
}
|
|
195
141
|
}
|
|
196
|
-
if (!result.messagePropNode && !result.descriptionNode && !result.idPropNode && hasSpreadAttribute)
|
|
197
|
-
return;
|
|
198
|
-
}
|
|
142
|
+
if (!result.messagePropNode && !result.descriptionNode && !result.idPropNode && hasSpreadAttribute) return;
|
|
199
143
|
return [result, values];
|
|
200
144
|
}
|
|
201
145
|
function extractMessageDescriptors(node) {
|
|
202
|
-
if (!node || node.type !== "ObjectExpression" || !node.properties.length)
|
|
203
|
-
return [];
|
|
204
|
-
}
|
|
146
|
+
if (!node || node.type !== "ObjectExpression" || !node.properties.length) return [];
|
|
205
147
|
const msgs = [];
|
|
206
148
|
for (const prop of node.properties) {
|
|
207
|
-
if (prop.type !== "Property")
|
|
208
|
-
continue;
|
|
209
|
-
}
|
|
149
|
+
if (prop.type !== "Property") continue;
|
|
210
150
|
const msg = prop.value;
|
|
211
|
-
if (msg.type !== "ObjectExpression")
|
|
212
|
-
continue;
|
|
213
|
-
}
|
|
151
|
+
if (msg.type !== "ObjectExpression") continue;
|
|
214
152
|
const nodeInfo = extractMessageDescriptor(msg);
|
|
215
|
-
if (nodeInfo)
|
|
216
|
-
msgs.push(nodeInfo);
|
|
217
|
-
}
|
|
153
|
+
if (nodeInfo) msgs.push(nodeInfo);
|
|
218
154
|
}
|
|
219
155
|
return msgs;
|
|
220
156
|
}
|
|
221
|
-
|
|
157
|
+
function extractMessages(node, { additionalComponentNames, additionalFunctionNames, excludeMessageDeclCalls } = {}) {
|
|
222
158
|
const allFormatFunctionNames = Array.isArray(additionalFunctionNames) ? new Set([...Array.from(FORMAT_FUNCTION_NAMES), ...additionalFunctionNames]) : FORMAT_FUNCTION_NAMES;
|
|
223
159
|
const allComponentNames = Array.isArray(additionalComponentNames) ? new Set([...Array.from(COMPONENT_NAMES), ...additionalComponentNames]) : COMPONENT_NAMES;
|
|
224
160
|
if (node.type === "CallExpression") {
|
|
225
161
|
const args0 = node.arguments[0];
|
|
226
162
|
const args1 = node.arguments[1];
|
|
227
|
-
|
|
228
|
-
if (!args0 || args0.type === "SpreadElement") {
|
|
229
|
-
return [];
|
|
230
|
-
}
|
|
163
|
+
if (!args0 || args0.type === "SpreadElement") return [];
|
|
231
164
|
if (!excludeMessageDeclCalls && isSingleMessageDescriptorDeclaration(node, DECLARATION_FUNCTION_NAMES) || isIntlFormatMessageCall(node) || isSingleMessageDescriptorDeclaration(node, allFormatFunctionNames)) {
|
|
232
165
|
const msgDescriptorNodeInfo = extractMessageDescriptor(args0);
|
|
233
|
-
if (msgDescriptorNodeInfo && (!args1 || args1.type !== "SpreadElement"))
|
|
234
|
-
|
|
235
|
-
}
|
|
236
|
-
} else if (!excludeMessageDeclCalls && isMultipleMessageDescriptorDeclaration(node)) {
|
|
237
|
-
return extractMessageDescriptors(args0).map((msg) => [msg, undefined]);
|
|
238
|
-
}
|
|
166
|
+
if (msgDescriptorNodeInfo && (!args1 || args1.type !== "SpreadElement")) return [[msgDescriptorNodeInfo, args1]];
|
|
167
|
+
} else if (!excludeMessageDeclCalls && isMultipleMessageDescriptorDeclaration(node)) return extractMessageDescriptors(args0).map((msg) => [msg, void 0]);
|
|
239
168
|
} else if (node.type === "JSXOpeningElement" && node.name && node.name.type === "JSXIdentifier" && allComponentNames.has(node.name.name)) {
|
|
240
169
|
const msgDescriptorNodeInfo = extractMessageDescriptorFromJSXElement(node);
|
|
241
|
-
if (msgDescriptorNodeInfo)
|
|
242
|
-
return [msgDescriptorNodeInfo];
|
|
243
|
-
}
|
|
170
|
+
if (msgDescriptorNodeInfo) return [msgDescriptorNodeInfo];
|
|
244
171
|
}
|
|
245
172
|
return [];
|
|
246
173
|
}
|
|
@@ -249,11 +176,12 @@ export function extractMessages(node, { additionalComponentNames, additionalFunc
|
|
|
249
176
|
* `fixer.replaceText(messageNode, <return value>)`. If the return value is null,
|
|
250
177
|
* it means that the patch cannot be applied.
|
|
251
178
|
*/
|
|
252
|
-
|
|
253
|
-
if (messageNode.type === "Literal" && messageNode.value && typeof messageNode.value === "string")
|
|
254
|
-
|
|
255
|
-
} else if (messageNode.type === "TemplateLiteral" && messageNode.quasis.length === 1 && messageNode.expressions.length === 0) {
|
|
256
|
-
return "`" + patcher(messageNode.quasis[0].value.cooked, ast).replace(/\\/g, "\\\\").replace(/`/g, "\\`") + "`";
|
|
257
|
-
}
|
|
179
|
+
function patchMessage(messageNode, ast, patcher) {
|
|
180
|
+
if (messageNode.type === "Literal" && messageNode.value && typeof messageNode.value === "string") return "\"" + patcher(messageNode.value, ast).replace("\"", "\\\"") + "\"";
|
|
181
|
+
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, "\\`") + "`";
|
|
258
182
|
return null;
|
|
259
183
|
}
|
|
184
|
+
//#endregion
|
|
185
|
+
export { extractMessageDescriptor, extractMessages, getSettings, isIntlFormatMessageCall, patchMessage };
|
|
186
|
+
|
|
187
|
+
//# sourceMappingURL=util.js.map
|
package/util.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.js","names":[],"sources":["../util.ts"],"sourcesContent":["import type {MessageFormatElement} from '@formatjs/icu-messageformat-parser'\nimport type {Rule} from 'eslint'\nimport type {\n BinaryExpression,\n Expression,\n Literal,\n Node,\n ObjectExpression,\n Property,\n TemplateLiteral,\n} from 'estree-jsx'\nimport type {JSXAttribute, JSXOpeningElement} from 'estree-jsx'\n\nexport interface MessageDescriptor {\n id?: string\n defaultMessage?: string\n description?: string | object\n}\n\nconst FORMAT_FUNCTION_NAMES = new Set(['$formatMessage', 'formatMessage', '$t'])\nconst COMPONENT_NAMES = new Set(['FormattedMessage'])\nconst DECLARATION_FUNCTION_NAMES = new Set(['defineMessage'])\n\nexport interface Settings {\n excludeMessageDeclCalls?: boolean\n additionalFunctionNames?: string[]\n additionalComponentNames?: string[]\n ignoreTag?: boolean\n}\nexport interface MessageDescriptorNodeInfo {\n message: MessageDescriptor\n messageDescriptorNode: ObjectExpression | JSXOpeningElement\n messageNode?: Property['value'] | JSXAttribute['value']\n messagePropNode?: Property | JSXAttribute\n descriptionNode?: Property['value'] | JSXAttribute['value']\n idValueNode?: Property['value'] | JSXAttribute['value']\n idPropNode?: Property | JSXAttribute\n}\n\nexport function getSettings({settings}: Rule.RuleContext): Settings {\n return settings.formatjs ?? settings\n}\n\nfunction isStringLiteral(node: Node): node is Literal & {value: string} {\n return node.type === 'Literal' && typeof node.value === 'string'\n}\n\nfunction isTemplateLiteralWithoutVar(node: Node): node is TemplateLiteral {\n return node.type === 'TemplateLiteral' && node.quasis.length === 1\n}\n\nfunction staticallyEvaluateStringConcat(\n node: BinaryExpression\n): [result: string, isStaticallyEvaluatable: boolean] {\n const right = node.right as Node\n const left = node.left as Node\n if (!isStringLiteral(right)) {\n return ['', false]\n }\n if (isStringLiteral(left)) {\n return [left.value + right.value, true]\n }\n if (node.left.type === 'BinaryExpression') {\n const [result, isStaticallyEvaluatable] = staticallyEvaluateStringConcat(\n node.left\n )\n return [result + right.value, isStaticallyEvaluatable]\n }\n return ['', false]\n}\n\nexport function isIntlFormatMessageCall(node: Node): boolean {\n // GH #4890: Check for both MemberExpression (intl.formatMessage) and Identifier (formatMessage) patterns\n if (node.type !== 'CallExpression') {\n return false\n }\n\n // Check if call has at least one argument that is an object expression\n if (\n node.arguments.length < 1 ||\n node.arguments[0].type !== 'ObjectExpression'\n ) {\n return false\n }\n\n // Pattern 1: intl.formatMessage() or something.intl.formatMessage()\n if (node.callee.type === 'MemberExpression') {\n return (\n ((node.callee.object.type === 'Identifier' &&\n node.callee.object.name === 'intl') ||\n (node.callee.object.type === 'MemberExpression' &&\n node.callee.object.property.type === 'Identifier' &&\n node.callee.object.property.name === 'intl')) &&\n node.callee.property.type === 'Identifier' &&\n (node.callee.property.name === 'formatMessage' ||\n node.callee.property.name === '$t')\n )\n }\n\n // Pattern 2: formatMessage() (destructured from useIntl)\n if (node.callee.type === 'Identifier') {\n return FORMAT_FUNCTION_NAMES.has(node.callee.name)\n }\n\n return false\n}\n\nfunction isSingleMessageDescriptorDeclaration(\n node: Node,\n functionNames: Set<string>\n) {\n return (\n node.type === 'CallExpression' &&\n node.callee.type === 'Identifier' &&\n functionNames.has(node.callee.name)\n )\n}\n\nfunction isMultipleMessageDescriptorDeclaration(node: Node) {\n return (\n node.type === 'CallExpression' &&\n node.callee.type === 'Identifier' &&\n node.callee.name === 'defineMessages'\n )\n}\n\nexport function extractMessageDescriptor(\n node?: Expression\n): MessageDescriptorNodeInfo | undefined {\n if (!node || node.type !== 'ObjectExpression') {\n return\n }\n const result: MessageDescriptorNodeInfo = {\n messageDescriptorNode: node,\n message: {},\n messageNode: undefined,\n messagePropNode: undefined,\n descriptionNode: undefined,\n idValueNode: undefined,\n }\n for (const prop of node.properties) {\n if (prop.type !== 'Property' || prop.key.type !== 'Identifier') {\n continue\n }\n\n // Only extract values for message-related props\n // GH #5069: Don't process other props like tagName, values, etc.\n const propName = prop.key.name\n if (\n propName !== 'id' &&\n propName !== 'defaultMessage' &&\n propName !== 'description'\n ) {\n continue\n }\n\n const valueNode = prop.value\n let value: string | undefined = undefined\n if (isStringLiteral(valueNode as Node)) {\n value = (valueNode as Literal & {value: string}).value\n }\n // like \"`asd`\"\n else if (isTemplateLiteralWithoutVar(valueNode as Node)) {\n value = (valueNode as TemplateLiteral).quasis[0].value.cooked ?? undefined\n }\n // like \"dedent`asd`\"\n else if (valueNode.type === 'TaggedTemplateExpression') {\n const {quasi} = valueNode\n if (!isTemplateLiteralWithoutVar(quasi as Node)) {\n throw new Error('Tagged template expression must be no substitution')\n }\n value = quasi.quasis[0].value.cooked ?? undefined\n }\n // like \"`asd` + `asd`\"\n else if (valueNode.type === 'BinaryExpression') {\n const [result, isStatic] = staticallyEvaluateStringConcat(valueNode)\n if (isStatic) {\n value = result\n }\n }\n\n switch (propName) {\n case 'defaultMessage':\n result.messagePropNode = prop\n result.messageNode = valueNode\n result.message.defaultMessage = value\n break\n case 'description':\n result.descriptionNode = valueNode\n result.message.description = value\n break\n case 'id':\n result.message.id = value\n result.idValueNode = valueNode\n result.idPropNode = prop\n break\n }\n }\n return result\n}\n\nfunction extractMessageDescriptorFromJSXElement(\n node?: JSXOpeningElement\n): [MessageDescriptorNodeInfo, ObjectExpression | undefined] | undefined {\n if (!node || !node.attributes) {\n return\n }\n let values: ObjectExpression | undefined\n const result: MessageDescriptorNodeInfo = {\n messageDescriptorNode: node,\n message: {},\n messageNode: undefined,\n messagePropNode: undefined,\n descriptionNode: undefined,\n idValueNode: undefined,\n idPropNode: undefined,\n }\n let hasSpreadAttribute = false\n for (const prop of node.attributes) {\n // We can't analyze spread attr\n if (prop.type === 'JSXSpreadAttribute') {\n hasSpreadAttribute = true\n }\n if (prop.type !== 'JSXAttribute' || prop.name.type !== 'JSXIdentifier') {\n continue\n }\n const key = prop.name\n const keyName = key.name\n\n // Only extract values for message-related props\n // GH #5069: Don't process other props like tagName, values, etc.\n // Allow them to have tagged templates with substitutions\n const isMessageProp =\n keyName === 'id' ||\n keyName === 'defaultMessage' ||\n keyName === 'description'\n\n let valueNode = prop.value\n let value: string | undefined = undefined\n if (valueNode && isMessageProp) {\n if (isStringLiteral(valueNode as Node)) {\n value = (valueNode as Literal & {value: string}).value\n } else if (valueNode?.type === 'JSXExpressionContainer') {\n const {expression} = valueNode\n if (expression.type === 'BinaryExpression') {\n const [result, isStatic] = staticallyEvaluateStringConcat(expression)\n if (isStatic) {\n value = result\n }\n }\n // like \"`asd`\"\n else if (isTemplateLiteralWithoutVar(expression as Node)) {\n value =\n (expression as TemplateLiteral).quasis[0].value.cooked ?? undefined\n }\n // like \"dedent`asd`\"\n else if (expression.type === 'TaggedTemplateExpression') {\n const {quasi} = expression\n if (!isTemplateLiteralWithoutVar(quasi as Node)) {\n throw new Error(\n 'Tagged template expression must be no substitution'\n )\n }\n value = quasi.quasis[0].value.cooked ?? undefined\n }\n }\n }\n\n switch (keyName) {\n case 'defaultMessage':\n result.messagePropNode = prop\n result.messageNode = valueNode\n if (value) {\n result.message.defaultMessage = value\n }\n break\n case 'description':\n result.descriptionNode = valueNode\n if (value) {\n result.message.description = value\n }\n break\n case 'id':\n result.idValueNode = valueNode\n result.idPropNode = prop\n if (value) {\n result.message.id = value\n }\n break\n case 'values':\n if (\n valueNode?.type === 'JSXExpressionContainer' &&\n valueNode.expression.type === 'ObjectExpression'\n ) {\n values = valueNode.expression\n }\n break\n }\n }\n if (\n !result.messagePropNode &&\n !result.descriptionNode &&\n !result.idPropNode &&\n hasSpreadAttribute\n ) {\n return\n }\n return [result, values]\n}\n\nfunction extractMessageDescriptors(node?: Expression) {\n if (!node || node.type !== 'ObjectExpression' || !node.properties.length) {\n return []\n }\n const msgs = []\n for (const prop of node.properties) {\n if (prop.type !== 'Property') {\n continue\n }\n const msg = prop.value\n if (msg.type !== 'ObjectExpression') {\n continue\n }\n const nodeInfo = extractMessageDescriptor(msg as Expression)\n if (nodeInfo) {\n msgs.push(nodeInfo)\n }\n }\n return msgs\n}\n\nexport function extractMessages(\n node: Node,\n {\n additionalComponentNames,\n additionalFunctionNames,\n excludeMessageDeclCalls,\n }: Settings = {}\n): Array<[MessageDescriptorNodeInfo, Expression | undefined]> {\n const allFormatFunctionNames = Array.isArray(additionalFunctionNames)\n ? new Set([\n ...Array.from(FORMAT_FUNCTION_NAMES),\n ...additionalFunctionNames,\n ])\n : FORMAT_FUNCTION_NAMES\n const allComponentNames = Array.isArray(additionalComponentNames)\n ? new Set([...Array.from(COMPONENT_NAMES), ...additionalComponentNames])\n : COMPONENT_NAMES\n if (node.type === 'CallExpression') {\n const args0 = node.arguments[0]\n const args1 = node.arguments[1]\n // We can't really analyze spread element\n if (!args0 || args0.type === 'SpreadElement') {\n return []\n }\n if (\n (!excludeMessageDeclCalls &&\n isSingleMessageDescriptorDeclaration(\n node,\n DECLARATION_FUNCTION_NAMES\n )) ||\n isIntlFormatMessageCall(node) ||\n isSingleMessageDescriptorDeclaration(node, allFormatFunctionNames)\n ) {\n const msgDescriptorNodeInfo = extractMessageDescriptor(args0)\n if (msgDescriptorNodeInfo && (!args1 || args1.type !== 'SpreadElement')) {\n return [[msgDescriptorNodeInfo, args1 as Expression]]\n }\n } else if (\n !excludeMessageDeclCalls &&\n isMultipleMessageDescriptorDeclaration(node)\n ) {\n return extractMessageDescriptors(args0).map(msg => [msg, undefined])\n }\n } else if (\n node.type === 'JSXOpeningElement' &&\n node.name &&\n node.name.type === 'JSXIdentifier' &&\n allComponentNames.has(node.name.name)\n ) {\n const msgDescriptorNodeInfo = extractMessageDescriptorFromJSXElement(node)\n if (msgDescriptorNodeInfo) {\n return [msgDescriptorNodeInfo]\n }\n }\n return []\n}\n\n/**\n * Apply changes to the ICU message in code. The return value can be used in\n * `fixer.replaceText(messageNode, <return value>)`. If the return value is null,\n * it means that the patch cannot be applied.\n */\nexport function patchMessage(\n messageNode: Node,\n ast: MessageFormatElement[],\n patcher: (messageContent: string, ast: MessageFormatElement[]) => string\n): string | null {\n if (\n messageNode.type === 'Literal' &&\n messageNode.value &&\n typeof messageNode.value === 'string'\n ) {\n return (\n '\"' + patcher(messageNode.value as string, ast).replace('\"', '\\\\\"') + '\"'\n )\n } else if (\n messageNode.type === 'TemplateLiteral' &&\n messageNode.quasis.length === 1 &&\n messageNode.expressions.length === 0\n ) {\n return (\n '`' +\n patcher(messageNode.quasis[0].value.cooked!, ast)\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/`/g, '\\\\`') +\n '`'\n )\n }\n\n return null\n}\n"],"mappings":"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"}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Auto-generated emoji version data from Unicode emoji-data.txt
|
|
3
|
-
* DO NOT EDIT MANUALLY - Generated by scripts/generate-emoji-data.ts
|
|
4
|
-
*
|
|
5
|
-
* Unicode Version: 17.0.0
|
|
6
|
-
* Ranges: 418 emoji codepoint ranges
|
|
7
|
-
*/
|
|
8
|
-
export type EmojiVersion = "0.0" | "0.6" | "0.7" | "1.0" | "2.0" | "3.0" | "4.0" | "5.0" | "11.0" | "12.0" | "13.0" | "14.0" | "15.0" | "16.0" | "17.0";
|
|
9
|
-
export declare const EMOJI_VERSIONS: EmojiVersion[];
|
|
10
|
-
export interface EmojiRange {
|
|
11
|
-
/** Start codepoint (decimal) */
|
|
12
|
-
start: number;
|
|
13
|
-
/** End codepoint (decimal), same as start for single codepoint */
|
|
14
|
-
end: number;
|
|
15
|
-
/** Unicode emoji version */
|
|
16
|
-
version: EmojiVersion;
|
|
17
|
-
/** Description */
|
|
18
|
-
name: string;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Emoji codepoint ranges with their Unicode versions
|
|
22
|
-
* Total ranges: 418
|
|
23
|
-
*
|
|
24
|
-
* To check if a codepoint belongs to a version, iterate through ranges
|
|
25
|
-
* and check if codepoint >= start && codepoint <= end
|
|
26
|
-
*/
|
|
27
|
-
export declare const EMOJI_RANGES: readonly EmojiRange[];
|