babel-plugin-formatjs 11.3.2 → 11.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.ts CHANGED
@@ -1,9 +1,37 @@
1
- import { type PluginObj, type PluginPass } from "@babel/core";
2
- import { type ExtractedMessageDescriptor, type Options } from "./types.js";
3
- export type ExtractionResult<M = Record<string, string>> = {
4
- messages: ExtractedMessageDescriptor[];
5
- meta: M;
1
+ import { SourceLocation } from "@babel/types";
2
+ import { PluginObj, PluginPass } from "@babel/core";
3
+
4
+ //#region packages/babel-plugin-formatjs/types.d.ts
5
+ interface MessageDescriptor {
6
+ id: string;
7
+ defaultMessage?: string;
8
+ description?: string;
9
+ }
10
+ type ExtractedMessageDescriptor = MessageDescriptor & Partial<SourceLocation> & {
11
+ file?: string;
6
12
  };
7
- export declare const DEFAULT_ID_INTERPOLATION_PATTERN = "[sha512:contenthash:base64:6]";
13
+ interface Options {
14
+ overrideIdFn?: (id?: string, defaultMessage?: string, description?: string, filePath?: string) => string;
15
+ onMsgExtracted?: (filePath: string, msgs: MessageDescriptor[]) => void;
16
+ onMetaExtracted?: (filePath: string, meta: Record<string, string>) => void;
17
+ idInterpolationPattern?: string;
18
+ removeDefaultMessage?: boolean;
19
+ additionalComponentNames?: string[];
20
+ additionalFunctionNames?: string[];
21
+ pragma?: string;
22
+ extractSourceLocation?: boolean;
23
+ ast?: boolean;
24
+ preserveWhitespace?: boolean;
25
+ flatten?: boolean;
26
+ }
27
+ //#endregion
28
+ //#region packages/babel-plugin-formatjs/index.d.ts
29
+ type ExtractionResult<M = Record<string, string>> = {
30
+ messages: ExtractedMessageDescriptor[];
31
+ meta: M;
32
+ };
33
+ declare const DEFAULT_ID_INTERPOLATION_PATTERN = "[sha512:contenthash:base64:6]";
8
34
  declare const plugin: (api: object, options: Options | null | undefined, dirname: string) => PluginObj<PluginPass>;
9
- export default plugin;
35
+ //#endregion
36
+ export { DEFAULT_ID_INTERPOLATION_PATTERN, ExtractionResult, plugin as default };
37
+ //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -1,24 +1,249 @@
1
- import "@babel/core";
2
1
  import { declare } from "@babel/helper-plugin-utils";
3
2
  import babelPluginSyntaxJsxNs from "@babel/plugin-syntax-jsx";
4
- import "./types.js";
5
- import { visitor as CallExpression } from "./visitors/call-expression.js";
6
- import { visitor as JSXOpeningElement } from "./visitors/jsx-opening-element.js";
3
+ import * as t from "@babel/types";
4
+ import { parse } from "@formatjs/icu-messageformat-parser";
5
+ import { hoistSelectors } from "@formatjs/icu-messageformat-parser/manipulator.js";
6
+ import { printAST } from "@formatjs/icu-messageformat-parser/printer.js";
7
+ import { interpolateName } from "@formatjs/ts-transformer";
8
+ //#region packages/babel-plugin-formatjs/utils.ts
9
+ const DESCRIPTOR_PROPS = new Set([
10
+ "id",
11
+ "description",
12
+ "defaultMessage"
13
+ ]);
14
+ function evaluatePath(path) {
15
+ const evaluated = path.evaluate();
16
+ if (evaluated.confident) return evaluated.value;
17
+ throw path.buildCodeFrameError("[React Intl] Messages must be statically evaluate-able for extraction.");
18
+ }
19
+ function getMessageDescriptorKey(path) {
20
+ if (path.isIdentifier() || path.isJSXIdentifier()) return path.node.name;
21
+ return evaluatePath(path);
22
+ }
23
+ function getMessageDescriptorValue(path, isMessageNode) {
24
+ if (!path) return "";
25
+ if (path.isJSXExpressionContainer()) {
26
+ if (isMessageNode && path.get("expression").isArrayExpression()) return "";
27
+ path = path.get("expression");
28
+ }
29
+ return evaluatePath(path);
30
+ }
31
+ function createMessageDescriptor(propPaths) {
32
+ return propPaths.reduce((hash, [keyPath, valuePath]) => {
33
+ const key = getMessageDescriptorKey(keyPath);
34
+ if (DESCRIPTOR_PROPS.has(key)) hash[key] = valuePath;
35
+ return hash;
36
+ }, {
37
+ id: void 0,
38
+ defaultMessage: void 0,
39
+ description: void 0
40
+ });
41
+ }
42
+ function evaluateMessageDescriptor(descriptorPath, isJSXSource = false, filename, idInterpolationPattern, overrideIdFn, preserveWhitespace, flatten) {
43
+ let id = getMessageDescriptorValue(descriptorPath.id);
44
+ let defaultMessage = getICUMessageValue(descriptorPath.defaultMessage, { isJSXSource }, preserveWhitespace);
45
+ const description = getMessageDescriptorValue(descriptorPath.description);
46
+ if (flatten && defaultMessage) try {
47
+ defaultMessage = printAST(hoistSelectors(parse(defaultMessage)));
48
+ } catch (e) {
49
+ const loc = descriptorPath.defaultMessage?.node.loc;
50
+ const locationStr = loc ? ` at line ${loc.start.line}, column ${loc.start.column + 1}` : "";
51
+ throw new Error(`[formatjs] Cannot flatten message in file "${filename}"${locationStr}${id ? ` with id "${id}"` : ""}: ${e.message}\nMessage: ${defaultMessage}`);
52
+ }
53
+ if (overrideIdFn) id = overrideIdFn(id, defaultMessage, description, filename);
54
+ else if (!id && idInterpolationPattern && defaultMessage) id = interpolateName({ resourcePath: filename }, idInterpolationPattern, { content: description ? `${defaultMessage}#${description}` : defaultMessage });
55
+ const descriptor = { id };
56
+ if (description) descriptor.description = description;
57
+ if (defaultMessage) descriptor.defaultMessage = defaultMessage;
58
+ return descriptor;
59
+ }
60
+ function getICUMessageValue(messagePath, { isJSXSource = false } = {}, preserveWhitespace) {
61
+ if (!messagePath) return "";
62
+ let message = getMessageDescriptorValue(messagePath, true);
63
+ if (!preserveWhitespace) message = message.trim().replace(/\s+/gm, " ");
64
+ try {
65
+ parse(message);
66
+ } catch (parseError) {
67
+ if (isJSXSource && messagePath.isLiteral() && message.indexOf("\\\\") >= 0) throw messagePath.buildCodeFrameError("[React Intl] Message failed to parse. It looks like `\\`s were used for escaping, this won't work with JSX string literals. Wrap with `{}`. See: http://facebook.github.io/react/docs/jsx-gotchas.html");
68
+ throw messagePath.buildCodeFrameError(`[React Intl] Message failed to parse. See: https://formatjs.github.io/docs/core-concepts/icu-syntax\n${parseError}`);
69
+ }
70
+ return message;
71
+ }
72
+ const EXTRACTED = Symbol("FormatJSExtracted");
73
+ /**
74
+ * Tag a node as extracted
75
+ * Store this in the node itself so that multiple passes work. Specifically
76
+ * if we remove `description` in the 1st pass, 2nd pass will fail since
77
+ * it expect `description` to be there.
78
+ * HACK: We store this in the node instance since this persists across
79
+ * multiple plugin runs
80
+ * @param path
81
+ */
82
+ function tagAsExtracted(path) {
83
+ path.node[EXTRACTED] = true;
84
+ }
85
+ /**
86
+ * Check if a node was extracted
87
+ * @param path
88
+ */
89
+ function wasExtracted(path) {
90
+ return !!path.node[EXTRACTED];
91
+ }
92
+ /**
93
+ * Store a message in our global messages
94
+ * @param messageDescriptor
95
+ * @param path
96
+ * @param opts
97
+ * @param filename
98
+ * @param messages
99
+ */
100
+ function storeMessage({ id, description, defaultMessage }, path, { extractSourceLocation }, filename, messages) {
101
+ if (!id && !defaultMessage) throw path.buildCodeFrameError("[React Intl] Message Descriptors require an `id` or `defaultMessage`.");
102
+ let loc = {};
103
+ if (extractSourceLocation) loc = {
104
+ file: filename,
105
+ ...path.node.loc
106
+ };
107
+ messages.push({
108
+ id,
109
+ description,
110
+ defaultMessage,
111
+ ...loc
112
+ });
113
+ }
114
+ //#endregion
115
+ //#region packages/babel-plugin-formatjs/visitors/call-expression.ts
116
+ function assertObjectExpression(path, callee) {
117
+ if (!path || !path.isObjectExpression()) throw path.buildCodeFrameError(`[React Intl] \`${callee.get("property").node.name}()\` must be called with an object expression with values that are React Intl Message Descriptors, also defined as object expressions.`);
118
+ }
119
+ function isFormatMessageCall(callee, functionNames) {
120
+ if (functionNames.find((name) => callee.isIdentifier({ name }))) return true;
121
+ if (callee.isMemberExpression() || callee.isOptionalMemberExpression()) {
122
+ const property = callee.get("property");
123
+ return !!functionNames.find((name) => property.isIdentifier({ name }));
124
+ }
125
+ return false;
126
+ }
127
+ function getMessagesObjectFromExpression(nodePath) {
128
+ let currentPath = nodePath;
129
+ while (t.isTSAsExpression(currentPath.node) || t.isTSTypeAssertion(currentPath.node) || t.isTypeCastExpression(currentPath.node)) currentPath = currentPath.get("expression");
130
+ return currentPath;
131
+ }
132
+ const visitor$1 = function(path, { opts, file: { opts: { filename } } }) {
133
+ const { overrideIdFn, idInterpolationPattern, removeDefaultMessage, ast, preserveWhitespace, flatten } = opts;
134
+ if (wasExtracted(path)) return;
135
+ const { messages, functionNames } = this;
136
+ const callee = path.get("callee");
137
+ const args = path.get("arguments");
138
+ /**
139
+ * Process MessageDescriptor
140
+ * @param messageDescriptor Message Descriptor
141
+ */
142
+ function processMessageObject(messageDescriptor) {
143
+ assertObjectExpression(messageDescriptor, callee);
144
+ const properties = messageDescriptor.get("properties");
145
+ const descriptorPath = createMessageDescriptor(properties.map((prop) => [prop.get("key"), prop.get("value")]));
146
+ if (descriptorPath.defaultMessage?.isArrayExpression()) return;
147
+ const descriptor = evaluateMessageDescriptor(descriptorPath, false, filename || void 0, idInterpolationPattern, overrideIdFn, preserveWhitespace, flatten);
148
+ storeMessage(descriptor, messageDescriptor, opts, filename || void 0, messages);
149
+ const firstProp = properties[0];
150
+ const defaultMessageProp = properties.find((prop) => {
151
+ const keyProp = prop.get("key");
152
+ return keyProp.isIdentifier({ name: "defaultMessage" }) || keyProp.isStringLiteral({ value: "defaultMessage" });
153
+ });
154
+ const idProp = properties.find((prop) => {
155
+ const keyProp = prop.get("key");
156
+ return keyProp.isIdentifier({ name: "id" }) || keyProp.isStringLiteral({ value: "id" });
157
+ });
158
+ if (idProp) idProp.get("value").replaceWith(t.stringLiteral(descriptor.id));
159
+ else firstProp.insertBefore(t.objectProperty(t.identifier("id"), t.stringLiteral(descriptor.id)));
160
+ properties.find((prop) => {
161
+ const keyProp = prop.get("key");
162
+ return keyProp.isIdentifier({ name: "description" }) || keyProp.isStringLiteral({ value: "description" });
163
+ })?.remove();
164
+ if (defaultMessageProp) {
165
+ if (removeDefaultMessage) defaultMessageProp?.remove();
166
+ else if (descriptor.defaultMessage) {
167
+ const valueProp = defaultMessageProp.get("value");
168
+ if (ast) valueProp.replaceWithSourceString(JSON.stringify(parse(descriptor.defaultMessage)));
169
+ else valueProp.replaceWith(t.stringLiteral(descriptor.defaultMessage));
170
+ }
171
+ }
172
+ tagAsExtracted(path);
173
+ }
174
+ if (callee.isIdentifier({ name: "defineMessages" }) || callee.isIdentifier({ name: "defineMessage" })) {
175
+ const firstArgument = args[0];
176
+ const messagesObj = getMessagesObjectFromExpression(firstArgument);
177
+ assertObjectExpression(messagesObj, callee);
178
+ if (callee.isIdentifier({ name: "defineMessage" })) processMessageObject(messagesObj);
179
+ else {
180
+ const properties = messagesObj.get("properties");
181
+ if (Array.isArray(properties)) properties.map((prop) => prop.get("value")).forEach(processMessageObject);
182
+ }
183
+ }
184
+ if (isFormatMessageCall(callee, functionNames)) {
185
+ const messageDescriptor = args[0];
186
+ if (messageDescriptor && messageDescriptor.isObjectExpression()) processMessageObject(messageDescriptor);
187
+ }
188
+ };
189
+ //#endregion
190
+ //#region packages/babel-plugin-formatjs/visitors/jsx-opening-element.ts
191
+ const visitor = function(path, { opts, file: { opts: { filename } } }) {
192
+ const { removeDefaultMessage, idInterpolationPattern, overrideIdFn, ast, preserveWhitespace, flatten } = opts;
193
+ const { componentNames, messages } = this;
194
+ if (wasExtracted(path)) return;
195
+ const name = path.get("name");
196
+ if (!componentNames.find((n) => name.isJSXIdentifier({ name: n }))) return;
197
+ const attributes = path.get("attributes").filter((attr) => attr.isJSXAttribute());
198
+ const descriptorPath = createMessageDescriptor(attributes.map((attr) => [attr.get("name"), attr.get("value")]));
199
+ if (!descriptorPath.defaultMessage) return;
200
+ const descriptor = evaluateMessageDescriptor(descriptorPath, true, filename || void 0, idInterpolationPattern, overrideIdFn, preserveWhitespace, flatten);
201
+ storeMessage(descriptor, path, opts, filename || void 0, messages);
202
+ let idAttr;
203
+ let descriptionAttr;
204
+ let defaultMessageAttr;
205
+ const firstAttr = attributes[0];
206
+ for (const attr of attributes) {
207
+ if (!attr.isJSXAttribute()) continue;
208
+ switch (getMessageDescriptorKey(attr.get("name"))) {
209
+ case "description":
210
+ descriptionAttr = attr;
211
+ break;
212
+ case "defaultMessage":
213
+ defaultMessageAttr = attr;
214
+ break;
215
+ case "id":
216
+ idAttr = attr;
217
+ break;
218
+ }
219
+ }
220
+ if (overrideIdFn || descriptor.id && idInterpolationPattern) {
221
+ if (idAttr) idAttr.get("value").replaceWith(t.stringLiteral(descriptor.id));
222
+ else if (firstAttr) firstAttr.insertBefore(t.jsxAttribute(t.jsxIdentifier("id"), t.stringLiteral(descriptor.id)));
223
+ }
224
+ if (descriptionAttr) descriptionAttr.remove();
225
+ if (defaultMessageAttr) {
226
+ if (removeDefaultMessage) defaultMessageAttr.remove();
227
+ else if (ast && descriptor.defaultMessage) {
228
+ defaultMessageAttr.get("value").replaceWith(t.jsxExpressionContainer(t.nullLiteral()));
229
+ defaultMessageAttr.get("value").get("expression").replaceWithSourceString(JSON.stringify(parse(descriptor.defaultMessage)));
230
+ }
231
+ }
232
+ tagAsExtracted(path);
233
+ };
234
+ //#endregion
235
+ //#region packages/babel-plugin-formatjs/index.ts
7
236
  const babelPluginSyntaxJsx = babelPluginSyntaxJsxNs.default || babelPluginSyntaxJsxNs;
8
- export const DEFAULT_ID_INTERPOLATION_PATTERN = "[sha512:contenthash:base64:6]";
237
+ const DEFAULT_ID_INTERPOLATION_PATTERN = "[sha512:contenthash:base64:6]";
9
238
  const plugin = declare((api, options) => {
10
239
  api.assertVersion(7);
11
- if (!options.idInterpolationPattern) {
12
- options.idInterpolationPattern = DEFAULT_ID_INTERPOLATION_PATTERN;
13
- }
240
+ if (!options.idInterpolationPattern) options.idInterpolationPattern = DEFAULT_ID_INTERPOLATION_PATTERN;
14
241
  const { pragma } = options;
15
242
  const componentNames = new Set(options.additionalComponentNames);
16
243
  componentNames.add("FormattedMessage");
17
244
  const functionNames = new Set(options.additionalFunctionNames);
18
245
  functionNames.add("formatMessage");
19
- // Short hand
20
246
  functionNames.add("$t");
21
- // Vue
22
247
  functionNames.add("$formatMessage");
23
248
  return {
24
249
  inherits: babelPluginSyntaxJsx,
@@ -31,17 +256,11 @@ const plugin = declare((api, options) => {
31
256
  enter(path) {
32
257
  this.messages = [];
33
258
  this.meta = {};
34
- if (!pragma) {
35
- return;
36
- }
259
+ if (!pragma) return;
37
260
  for (const { leadingComments } of path.node.body) {
38
- if (!leadingComments) {
39
- continue;
40
- }
261
+ if (!leadingComments) continue;
41
262
  const pragmaLineNode = leadingComments.find((c) => c.value.includes(pragma));
42
- if (!pragmaLineNode) {
43
- continue;
44
- }
263
+ if (!pragmaLineNode) continue;
45
264
  pragmaLineNode.value.split(pragma)[1].trim().split(/\s+/g).forEach((kv) => {
46
265
  const [k, v] = kv.split(":");
47
266
  this.meta[k] = v;
@@ -50,18 +269,17 @@ const plugin = declare((api, options) => {
50
269
  },
51
270
  exit(_, { opts: _opts, file: { opts: { filename } } }) {
52
271
  const opts = _opts;
53
- if (typeof opts?.onMetaExtracted === "function") {
54
- opts.onMetaExtracted(filename || "", this.meta);
55
- }
56
- if (typeof opts?.onMsgExtracted === "function") {
57
- opts.onMsgExtracted(filename || "", this.messages);
58
- }
272
+ if (typeof opts?.onMetaExtracted === "function") opts.onMetaExtracted(filename || "", this.meta);
273
+ if (typeof opts?.onMsgExtracted === "function") opts.onMsgExtracted(filename || "", this.messages);
59
274
  }
60
275
  },
61
- JSXOpeningElement,
62
- CallExpression,
63
- OptionalCallExpression: CallExpression
276
+ JSXOpeningElement: visitor,
277
+ CallExpression: visitor$1,
278
+ OptionalCallExpression: visitor$1
64
279
  }
65
280
  };
66
281
  });
67
- export default plugin;
282
+ //#endregion
283
+ export { DEFAULT_ID_INTERPOLATION_PATTERN, plugin as default };
284
+
285
+ //# sourceMappingURL=index.js.map
package/index.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["visitor","CallExpression"],"sources":["../utils.ts","../visitors/call-expression.ts","../visitors/jsx-opening-element.ts","../index.ts"],"sourcesContent":["import type * as t from '@babel/types'\nimport {parse} from '@formatjs/icu-messageformat-parser'\nimport {hoistSelectors} from '@formatjs/icu-messageformat-parser/manipulator.js'\nimport {printAST} from '@formatjs/icu-messageformat-parser/printer.js'\nimport {interpolateName} from '@formatjs/ts-transformer'\n\nimport {type NodePath} from '@babel/core'\nimport {\n type ExtractedMessageDescriptor,\n type MessageDescriptor,\n type MessageDescriptorPath,\n type Options,\n} from '#packages/babel-plugin-formatjs/types.js'\n\nconst DESCRIPTOR_PROPS = new Set<keyof MessageDescriptorPath>([\n 'id',\n 'description',\n 'defaultMessage',\n])\n\nfunction evaluatePath(path: NodePath<any>): string {\n const evaluated = path.evaluate()\n if (evaluated.confident) {\n return evaluated.value\n }\n\n throw path.buildCodeFrameError(\n '[React Intl] Messages must be statically evaluate-able for extraction.'\n )\n}\n\nexport function getMessageDescriptorKey(path: NodePath<any>): string {\n if (path.isIdentifier() || path.isJSXIdentifier()) {\n return path.node.name\n }\n\n return evaluatePath(path)\n}\n\nfunction getMessageDescriptorValue(\n path?:\n | NodePath<t.StringLiteral>\n | NodePath<t.JSXExpressionContainer>\n | NodePath<t.TemplateLiteral>,\n isMessageNode?: boolean\n) {\n if (!path) {\n return ''\n }\n if (path.isJSXExpressionContainer()) {\n // If this is already compiled, no need to recompiled it\n if (isMessageNode && path.get('expression').isArrayExpression()) {\n return ''\n }\n path = path.get('expression') as NodePath<t.StringLiteral>\n }\n\n // Always trim the Message Descriptor values.\n const descriptorValue = evaluatePath(path)\n\n return descriptorValue\n}\n\nexport function createMessageDescriptor(\n propPaths: [\n NodePath<t.JSXIdentifier> | NodePath<t.Identifier>,\n NodePath<t.StringLiteral> | NodePath<t.JSXExpressionContainer>,\n ][]\n): MessageDescriptorPath {\n return propPaths.reduce(\n (hash: MessageDescriptorPath, [keyPath, valuePath]) => {\n const key = getMessageDescriptorKey(\n keyPath\n ) as keyof MessageDescriptorPath\n\n if (DESCRIPTOR_PROPS.has(key)) {\n hash[key] = valuePath\n }\n\n return hash\n },\n {\n id: undefined,\n defaultMessage: undefined,\n description: undefined,\n }\n )\n}\n\nexport function evaluateMessageDescriptor(\n descriptorPath: MessageDescriptorPath,\n isJSXSource = false,\n filename: string | undefined,\n idInterpolationPattern?: string,\n overrideIdFn?: Options['overrideIdFn'],\n preserveWhitespace?: Options['preserveWhitespace'],\n flatten?: Options['flatten']\n): MessageDescriptor {\n let id = getMessageDescriptorValue(descriptorPath.id)\n let defaultMessage = getICUMessageValue(\n descriptorPath.defaultMessage,\n {\n isJSXSource,\n },\n preserveWhitespace\n )\n const description = getMessageDescriptorValue(descriptorPath.description)\n\n // GH #3537: Apply flatten transformation before calling overrideIdFn\n // so that the ID generation sees the same message format as the final output\n if (flatten && defaultMessage) {\n try {\n defaultMessage = printAST(hoistSelectors(parse(defaultMessage)))\n } catch (e: any) {\n const loc = descriptorPath.defaultMessage?.node.loc\n const locationStr = loc\n ? ` at line ${loc.start.line}, column ${loc.start.column + 1}`\n : ''\n throw new Error(\n `[formatjs] Cannot flatten message in file \"${filename}\"${locationStr}${id ? ` with id \"${id}\"` : ''}: ${e.message}\\nMessage: ${defaultMessage}`\n )\n }\n }\n\n if (overrideIdFn) {\n id = overrideIdFn(id, defaultMessage, description, filename)\n } else if (!id && idInterpolationPattern && defaultMessage) {\n id = interpolateName(\n {resourcePath: filename} as any,\n idInterpolationPattern,\n {\n content: description\n ? `${defaultMessage}#${description}`\n : defaultMessage,\n }\n )\n }\n const descriptor: MessageDescriptor = {\n id,\n }\n\n if (description) {\n descriptor.description = description\n }\n if (defaultMessage) {\n descriptor.defaultMessage = defaultMessage\n }\n\n return descriptor\n}\n\nfunction getICUMessageValue(\n messagePath?:\n | NodePath<t.StringLiteral>\n | NodePath<t.TemplateLiteral>\n | NodePath<t.JSXExpressionContainer>,\n {isJSXSource = false} = {},\n preserveWhitespace?: Options['preserveWhitespace']\n) {\n if (!messagePath) {\n return ''\n }\n let message = getMessageDescriptorValue(messagePath, true)\n\n if (!preserveWhitespace) {\n message = message.trim().replace(/\\s+/gm, ' ')\n }\n\n try {\n parse(message)\n } catch (parseError) {\n if (\n isJSXSource &&\n messagePath.isLiteral() &&\n message.indexOf('\\\\\\\\') >= 0\n ) {\n throw messagePath.buildCodeFrameError(\n '[React Intl] Message failed to parse. ' +\n 'It looks like `\\\\`s were used for escaping, ' +\n \"this won't work with JSX string literals. \" +\n 'Wrap with `{}`. ' +\n 'See: http://facebook.github.io/react/docs/jsx-gotchas.html'\n )\n }\n\n throw messagePath.buildCodeFrameError(\n '[React Intl] Message failed to parse. ' +\n 'See: https://formatjs.github.io/docs/core-concepts/icu-syntax' +\n `\\n${parseError}`\n )\n }\n return message\n}\nconst EXTRACTED = Symbol('FormatJSExtracted')\n/**\n * Tag a node as extracted\n * Store this in the node itself so that multiple passes work. Specifically\n * if we remove `description` in the 1st pass, 2nd pass will fail since\n * it expect `description` to be there.\n * HACK: We store this in the node instance since this persists across\n * multiple plugin runs\n * @param path\n */\nexport function tagAsExtracted(path: NodePath<any>): void {\n path.node[EXTRACTED] = true\n}\n/**\n * Check if a node was extracted\n * @param path\n */\nexport function wasExtracted(path: NodePath<any>): boolean {\n return !!path.node[EXTRACTED]\n}\n\n/**\n * Store a message in our global messages\n * @param messageDescriptor\n * @param path\n * @param opts\n * @param filename\n * @param messages\n */\nexport function storeMessage(\n {id, description, defaultMessage}: MessageDescriptor,\n path: NodePath<any>,\n {extractSourceLocation}: Options,\n\n filename: string | undefined,\n messages: ExtractedMessageDescriptor[]\n): void {\n if (!id && !defaultMessage) {\n throw path.buildCodeFrameError(\n '[React Intl] Message Descriptors require an `id` or `defaultMessage`.'\n )\n }\n\n let loc = {}\n if (extractSourceLocation) {\n loc = {\n file: filename,\n ...path.node.loc,\n }\n }\n messages.push({id, description, defaultMessage, ...loc})\n}\n","import {type NodePath, type PluginPass} from '@babel/core'\nimport * as t from '@babel/types'\nimport {\n type Options,\n type State,\n} from '#packages/babel-plugin-formatjs/types.js'\nimport {type VisitNodeFunction} from '@babel/traverse'\nimport {\n createMessageDescriptor,\n evaluateMessageDescriptor,\n wasExtracted,\n storeMessage,\n tagAsExtracted,\n} from '#packages/babel-plugin-formatjs/utils.js'\nimport {parse} from '@formatjs/icu-messageformat-parser'\n\nfunction assertObjectExpression(\n path: NodePath<any>,\n callee: NodePath<t.Expression | t.V8IntrinsicIdentifier>\n): asserts path is NodePath<t.ObjectExpression> {\n if (!path || !path.isObjectExpression()) {\n throw path.buildCodeFrameError(\n `[React Intl] \\`${\n (callee.get('property') as NodePath<t.Identifier>).node.name\n }()\\` must be called with an object expression with values that are React Intl Message Descriptors, also defined as object expressions.`\n )\n }\n}\n\nfunction isFormatMessageCall(\n callee: NodePath<t.Expression | t.V8IntrinsicIdentifier | t.MemberExpression>,\n functionNames: string[]\n) {\n if (functionNames.find(name => callee.isIdentifier({name}))) {\n return true\n }\n\n // GH #4471: Handle both MemberExpression and OptionalMemberExpression\n // (e.g., intl.formatMessage() and intl.formatMessage?.())\n if (callee.isMemberExpression() || callee.isOptionalMemberExpression()) {\n const property = callee.get('property') as NodePath<t.MemberExpression>\n return !!functionNames.find(name => property.isIdentifier({name}))\n }\n return false\n}\n\nfunction getMessagesObjectFromExpression(\n nodePath: NodePath<any>\n): NodePath<any> {\n let currentPath = nodePath\n while (\n t.isTSAsExpression(currentPath.node) ||\n t.isTSTypeAssertion(currentPath.node) ||\n t.isTypeCastExpression(currentPath.node)\n ) {\n currentPath = currentPath.get('expression') as NodePath<any>\n }\n return currentPath\n}\n\nexport const visitor: VisitNodeFunction<PluginPass & State, t.CallExpression> =\n function (\n path,\n {\n opts,\n file: {\n opts: {filename},\n },\n }\n ) {\n const {\n overrideIdFn,\n idInterpolationPattern,\n removeDefaultMessage,\n ast,\n preserveWhitespace,\n flatten,\n } = opts as Options\n if (wasExtracted(path)) {\n return\n }\n const {messages, functionNames} = this\n const callee = path.get('callee')\n const args = path.get('arguments')\n\n /**\n * Process MessageDescriptor\n * @param messageDescriptor Message Descriptor\n */\n function processMessageObject(\n messageDescriptor: NodePath<t.ObjectExpression>\n ) {\n assertObjectExpression(messageDescriptor, callee)\n\n const properties = messageDescriptor.get(\n 'properties'\n ) as NodePath<t.ObjectProperty>[]\n\n const descriptorPath = createMessageDescriptor(\n properties.map(\n prop =>\n [prop.get('key'), prop.get('value')] as [\n NodePath<t.Identifier>,\n NodePath<t.StringLiteral>,\n ]\n )\n )\n\n // If the message is already compiled, don't re-compile it\n if (descriptorPath.defaultMessage?.isArrayExpression()) {\n return\n }\n\n // Evaluate the Message Descriptor values, then store it.\n const descriptor = evaluateMessageDescriptor(\n descriptorPath,\n false,\n filename || undefined,\n idInterpolationPattern,\n overrideIdFn,\n preserveWhitespace,\n flatten\n )\n storeMessage(\n descriptor,\n messageDescriptor,\n opts as Options,\n filename || undefined,\n messages\n )\n\n const firstProp = properties[0]\n const defaultMessageProp = properties.find(prop => {\n const keyProp = prop.get('key')\n return (\n keyProp.isIdentifier({name: 'defaultMessage'}) ||\n keyProp.isStringLiteral({value: 'defaultMessage'})\n )\n })\n const idProp = properties.find(prop => {\n const keyProp = prop.get('key')\n return (\n keyProp.isIdentifier({name: 'id'}) ||\n keyProp.isStringLiteral({value: 'id'})\n )\n })\n\n // Insert ID potentially 1st before removing nodes\n if (idProp) {\n idProp.get('value').replaceWith(t.stringLiteral(descriptor.id))\n } else {\n firstProp.insertBefore(\n t.objectProperty(t.identifier('id'), t.stringLiteral(descriptor.id))\n )\n }\n\n // Remove description\n properties\n .find(prop => {\n const keyProp = prop.get('key')\n return (\n keyProp.isIdentifier({name: 'description'}) ||\n keyProp.isStringLiteral({value: 'description'})\n )\n })\n ?.remove()\n\n // Pre-parse or remove defaultMessage\n if (defaultMessageProp) {\n if (removeDefaultMessage) {\n defaultMessageProp?.remove()\n } else if (descriptor.defaultMessage) {\n const valueProp = defaultMessageProp.get('value')\n if (ast) {\n valueProp.replaceWithSourceString(\n JSON.stringify(parse(descriptor.defaultMessage))\n )\n } else {\n valueProp.replaceWith(t.stringLiteral(descriptor.defaultMessage))\n }\n }\n }\n\n tagAsExtracted(path)\n }\n\n // Check that this is `defineMessages` call\n if (\n callee.isIdentifier({name: 'defineMessages'}) ||\n callee.isIdentifier({name: 'defineMessage'})\n ) {\n const firstArgument = args[0]\n const messagesObj = getMessagesObjectFromExpression(firstArgument)\n\n assertObjectExpression(messagesObj, callee)\n if (callee.isIdentifier({name: 'defineMessage'})) {\n processMessageObject(messagesObj as NodePath<t.ObjectExpression>)\n } else {\n const properties = messagesObj.get('properties')\n if (Array.isArray(properties)) {\n properties\n .map(prop => prop.get('value') as NodePath<t.ObjectExpression>)\n .forEach(processMessageObject)\n }\n }\n }\n\n // Check that this is `intl.formatMessage` call\n if (isFormatMessageCall(callee, functionNames)) {\n const messageDescriptor = args[0]\n if (messageDescriptor && messageDescriptor.isObjectExpression()) {\n processMessageObject(messageDescriptor)\n }\n }\n }\n","import {type NodePath, type PluginPass} from '@babel/core'\n\nimport {\n type Options,\n type State,\n} from '#packages/babel-plugin-formatjs/types.js'\nimport * as t from '@babel/types'\nimport {type VisitNodeFunction} from '@babel/traverse'\nimport {parse} from '@formatjs/icu-messageformat-parser'\nimport {\n createMessageDescriptor,\n evaluateMessageDescriptor,\n getMessageDescriptorKey,\n storeMessage,\n tagAsExtracted,\n wasExtracted,\n} from '#packages/babel-plugin-formatjs/utils.js'\n\nexport const visitor: VisitNodeFunction<\n PluginPass & State,\n t.JSXOpeningElement\n> = function (\n path,\n {\n opts,\n file: {\n opts: {filename},\n },\n }\n) {\n const {\n removeDefaultMessage,\n idInterpolationPattern,\n overrideIdFn,\n ast,\n preserveWhitespace,\n flatten,\n } = opts as Options\n\n const {componentNames, messages} = this\n if (wasExtracted(path)) {\n return\n }\n\n const name = path.get('name')\n\n if (!componentNames.find(n => name.isJSXIdentifier({name: n}))) {\n return\n }\n\n const attributes = path\n .get('attributes')\n .filter(attr => attr.isJSXAttribute())\n\n const descriptorPath = createMessageDescriptor(\n attributes.map(attr => [\n attr.get('name') as NodePath<t.JSXIdentifier>,\n attr.get('value') as\n | NodePath<t.StringLiteral>\n | NodePath<t.JSXExpressionContainer>,\n ])\n )\n\n // In order for a default message to be extracted when\n // declaring a JSX element, it must be done with standard\n // `key=value` attributes. But it's completely valid to\n // write `<FormattedMessage {...descriptor} />`, because it will be\n // skipped here and extracted elsewhere. The descriptor will\n // be extracted only (storeMessage) if a `defaultMessage` prop.\n if (!descriptorPath.defaultMessage) {\n return\n }\n\n // Evaluate the Message Descriptor values in a JSX\n // context, then store it.\n const descriptor = evaluateMessageDescriptor(\n descriptorPath,\n true,\n filename || undefined,\n idInterpolationPattern,\n overrideIdFn,\n preserveWhitespace,\n flatten\n )\n\n storeMessage(\n descriptor,\n path,\n opts as Options,\n filename || undefined,\n messages\n )\n\n let idAttr: NodePath<t.JSXAttribute> | undefined\n let descriptionAttr: NodePath<t.JSXAttribute> | undefined\n let defaultMessageAttr: NodePath<t.JSXAttribute> | undefined\n const firstAttr = attributes[0]\n for (const attr of attributes) {\n if (!attr.isJSXAttribute()) {\n continue\n }\n switch (\n getMessageDescriptorKey((attr as NodePath<t.JSXAttribute>).get('name'))\n ) {\n case 'description':\n descriptionAttr = attr\n break\n case 'defaultMessage':\n defaultMessageAttr = attr\n break\n case 'id':\n idAttr = attr\n break\n }\n }\n\n // Insert ID before removing node to prevent null node insertBefore\n if (overrideIdFn || (descriptor.id && idInterpolationPattern)) {\n if (idAttr) {\n idAttr.get('value').replaceWith(t.stringLiteral(descriptor.id))\n } else if (firstAttr) {\n firstAttr.insertBefore(\n t.jsxAttribute(t.jsxIdentifier('id'), t.stringLiteral(descriptor.id))\n )\n }\n }\n\n if (descriptionAttr) {\n descriptionAttr.remove()\n }\n\n if (defaultMessageAttr) {\n if (removeDefaultMessage) {\n defaultMessageAttr.remove()\n } else if (ast && descriptor.defaultMessage) {\n defaultMessageAttr\n .get('value')\n .replaceWith(t.jsxExpressionContainer(t.nullLiteral()))\n const valueAttr = defaultMessageAttr.get(\n 'value'\n ) as NodePath<t.JSXExpressionContainer>\n valueAttr\n .get('expression')\n .replaceWithSourceString(\n JSON.stringify(parse(descriptor.defaultMessage))\n )\n }\n }\n\n // Tag the AST node so we don't try to extract it twice.\n tagAsExtracted(path)\n}\n","import {type PluginObj, type PluginPass} from '@babel/core'\nimport {declare} from '@babel/helper-plugin-utils'\nimport babelPluginSyntaxJsxNs from '@babel/plugin-syntax-jsx'\nimport {\n type ExtractedMessageDescriptor,\n type Options,\n type State,\n} from '#packages/babel-plugin-formatjs/types.js'\nimport {visitor as CallExpression} from '#packages/babel-plugin-formatjs/visitors/call-expression.js'\nimport {visitor as JSXOpeningElement} from '#packages/babel-plugin-formatjs/visitors/jsx-opening-element.js'\n\nconst babelPluginSyntaxJsx =\n (babelPluginSyntaxJsxNs as any).default || babelPluginSyntaxJsxNs\n\nexport type ExtractionResult<M = Record<string, string>> = {\n messages: ExtractedMessageDescriptor[]\n meta: M\n}\n\nexport const DEFAULT_ID_INTERPOLATION_PATTERN = '[sha512:contenthash:base64:6]'\n\nconst plugin: (\n api: object,\n options: Options | null | undefined,\n dirname: string\n) => PluginObj<PluginPass> =\n // @ts-expect-error PluginPass doesn't allow custom state but it actually does\n declare<Options, PluginObj>((api, options) => {\n api.assertVersion(7)\n if (!options.idInterpolationPattern) {\n options.idInterpolationPattern = DEFAULT_ID_INTERPOLATION_PATTERN\n }\n\n const {pragma} = options\n const componentNames = new Set<string>(options.additionalComponentNames)\n componentNames.add('FormattedMessage')\n const functionNames = new Set<string>(options.additionalFunctionNames)\n functionNames.add('formatMessage')\n // Short hand\n functionNames.add('$t')\n // Vue\n functionNames.add('$formatMessage')\n return {\n inherits: babelPluginSyntaxJsx,\n pre() {\n this.componentNames = Array.from(componentNames)\n this.functionNames = Array.from(functionNames)\n },\n\n visitor: {\n Program: {\n enter(this: PluginPass & State, path) {\n this.messages = []\n this.meta = {}\n if (!pragma) {\n return\n }\n for (const {leadingComments} of path.node.body) {\n if (!leadingComments) {\n continue\n }\n const pragmaLineNode = leadingComments.find(c =>\n c.value.includes(pragma)\n )\n if (!pragmaLineNode) {\n continue\n }\n\n pragmaLineNode.value\n .split(pragma)[1]\n .trim()\n .split(/\\s+/g)\n .forEach(kv => {\n const [k, v] = kv.split(':')\n this.meta[k] = v\n })\n }\n },\n exit(\n this: PluginPass & State,\n _,\n {\n opts: _opts,\n file: {\n opts: {filename},\n },\n }\n ) {\n const opts = _opts as Options\n if (typeof opts?.onMetaExtracted === 'function') {\n opts.onMetaExtracted(filename || '', this.meta)\n }\n if (typeof opts?.onMsgExtracted === 'function') {\n opts.onMsgExtracted(filename || '', this.messages)\n }\n },\n },\n JSXOpeningElement,\n CallExpression,\n // GH #4471: Handle optional chaining calls (e.g., intl.formatMessage?.())\n OptionalCallExpression: CallExpression,\n },\n }\n })\nexport default plugin\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "babel-plugin-formatjs",
3
3
  "description": "Extracts string messages for translation from modules that use formatjs.",
4
- "version": "11.3.2",
4
+ "version": "11.3.3",
5
5
  "license": "MIT",
6
6
  "author": "Long Ho <holevietlong@gmail.com>",
7
7
  "type": "module",
@@ -18,8 +18,8 @@
18
18
  "@types/babel__core": "^7.20.5",
19
19
  "@types/babel__helper-plugin-utils": "^7.10.3",
20
20
  "@types/babel__traverse": "^7.28.0",
21
- "@formatjs/icu-messageformat-parser": "3.5.3",
22
- "@formatjs/ts-transformer": "4.4.3"
21
+ "@formatjs/icu-messageformat-parser": "3.5.4",
22
+ "@formatjs/ts-transformer": "4.4.4"
23
23
  },
24
24
  "bugs": "https://github.com/formatjs/formatjs/issues",
25
25
  "gitHead": "8b0baec8eda5002715cf893274fe59782fc2d371",
package/global.d.ts DELETED
@@ -1 +0,0 @@
1
- declare module '@babel/plugin-syntax-jsx'
package/types.d.ts DELETED
@@ -1,31 +0,0 @@
1
- import { type NodePath } from "@babel/core";
2
- import { type JSXExpressionContainer, type SourceLocation, type StringLiteral } from "@babel/types";
3
- export interface MessageDescriptor {
4
- id: string;
5
- defaultMessage?: string;
6
- description?: string;
7
- }
8
- export interface State {
9
- messages: ExtractedMessageDescriptor[];
10
- meta: Record<string, string>;
11
- componentNames: string[];
12
- functionNames: string[];
13
- }
14
- export type ExtractedMessageDescriptor = MessageDescriptor & Partial<SourceLocation> & {
15
- file?: string;
16
- };
17
- export type MessageDescriptorPath = Record<keyof MessageDescriptor, NodePath<StringLiteral> | NodePath<JSXExpressionContainer> | undefined>;
18
- export interface Options {
19
- overrideIdFn?: (id?: string, defaultMessage?: string, description?: string, filePath?: string) => string;
20
- onMsgExtracted?: (filePath: string, msgs: MessageDescriptor[]) => void;
21
- onMetaExtracted?: (filePath: string, meta: Record<string, string>) => void;
22
- idInterpolationPattern?: string;
23
- removeDefaultMessage?: boolean;
24
- additionalComponentNames?: string[];
25
- additionalFunctionNames?: string[];
26
- pragma?: string;
27
- extractSourceLocation?: boolean;
28
- ast?: boolean;
29
- preserveWhitespace?: boolean;
30
- flatten?: boolean;
31
- }
package/types.js DELETED
@@ -1,2 +0,0 @@
1
- import "@babel/core";
2
- import "@babel/types";
package/utils.d.ts DELETED
@@ -1,30 +0,0 @@
1
- import type * as t from "@babel/types";
2
- import { type NodePath } from "@babel/core";
3
- import { type ExtractedMessageDescriptor, type MessageDescriptor, type MessageDescriptorPath, type Options } from "./types.js";
4
- export declare function getMessageDescriptorKey(path: NodePath<any>): string;
5
- export declare function createMessageDescriptor(propPaths: [NodePath<t.JSXIdentifier> | NodePath<t.Identifier>, NodePath<t.StringLiteral> | NodePath<t.JSXExpressionContainer>][]): MessageDescriptorPath;
6
- export declare function evaluateMessageDescriptor(descriptorPath: MessageDescriptorPath, isJSXSource: boolean | undefined, filename: string | undefined, idInterpolationPattern?: string, overrideIdFn?: Options["overrideIdFn"], preserveWhitespace?: Options["preserveWhitespace"], flatten?: Options["flatten"]): MessageDescriptor;
7
- /**
8
- * Tag a node as extracted
9
- * Store this in the node itself so that multiple passes work. Specifically
10
- * if we remove `description` in the 1st pass, 2nd pass will fail since
11
- * it expect `description` to be there.
12
- * HACK: We store this in the node instance since this persists across
13
- * multiple plugin runs
14
- * @param path
15
- */
16
- export declare function tagAsExtracted(path: NodePath<any>): void;
17
- /**
18
- * Check if a node was extracted
19
- * @param path
20
- */
21
- export declare function wasExtracted(path: NodePath<any>): boolean;
22
- /**
23
- * Store a message in our global messages
24
- * @param messageDescriptor
25
- * @param path
26
- * @param opts
27
- * @param filename
28
- * @param messages
29
- */
30
- export declare function storeMessage({ id, description, defaultMessage }: MessageDescriptor, path: NodePath<any>, { extractSourceLocation }: Options, filename: string | undefined, messages: ExtractedMessageDescriptor[]): void;
package/utils.js DELETED
@@ -1,145 +0,0 @@
1
- import { parse } from "@formatjs/icu-messageformat-parser";
2
- import { hoistSelectors } from "@formatjs/icu-messageformat-parser/manipulator.js";
3
- import { printAST } from "@formatjs/icu-messageformat-parser/printer.js";
4
- import { interpolateName } from "@formatjs/ts-transformer";
5
- import "@babel/core";
6
- import "./types.js";
7
- const DESCRIPTOR_PROPS = new Set([
8
- "id",
9
- "description",
10
- "defaultMessage"
11
- ]);
12
- function evaluatePath(path) {
13
- const evaluated = path.evaluate();
14
- if (evaluated.confident) {
15
- return evaluated.value;
16
- }
17
- throw path.buildCodeFrameError("[React Intl] Messages must be statically evaluate-able for extraction.");
18
- }
19
- export function getMessageDescriptorKey(path) {
20
- if (path.isIdentifier() || path.isJSXIdentifier()) {
21
- return path.node.name;
22
- }
23
- return evaluatePath(path);
24
- }
25
- function getMessageDescriptorValue(path, isMessageNode) {
26
- if (!path) {
27
- return "";
28
- }
29
- if (path.isJSXExpressionContainer()) {
30
- // If this is already compiled, no need to recompiled it
31
- if (isMessageNode && path.get("expression").isArrayExpression()) {
32
- return "";
33
- }
34
- path = path.get("expression");
35
- }
36
- // Always trim the Message Descriptor values.
37
- const descriptorValue = evaluatePath(path);
38
- return descriptorValue;
39
- }
40
- export function createMessageDescriptor(propPaths) {
41
- return propPaths.reduce((hash, [keyPath, valuePath]) => {
42
- const key = getMessageDescriptorKey(keyPath);
43
- if (DESCRIPTOR_PROPS.has(key)) {
44
- hash[key] = valuePath;
45
- }
46
- return hash;
47
- }, {
48
- id: undefined,
49
- defaultMessage: undefined,
50
- description: undefined
51
- });
52
- }
53
- export function evaluateMessageDescriptor(descriptorPath, isJSXSource = false, filename, idInterpolationPattern, overrideIdFn, preserveWhitespace, flatten) {
54
- let id = getMessageDescriptorValue(descriptorPath.id);
55
- let defaultMessage = getICUMessageValue(descriptorPath.defaultMessage, { isJSXSource }, preserveWhitespace);
56
- const description = getMessageDescriptorValue(descriptorPath.description);
57
- // GH #3537: Apply flatten transformation before calling overrideIdFn
58
- // so that the ID generation sees the same message format as the final output
59
- if (flatten && defaultMessage) {
60
- try {
61
- defaultMessage = printAST(hoistSelectors(parse(defaultMessage)));
62
- } catch (e) {
63
- const loc = descriptorPath.defaultMessage?.node.loc;
64
- const locationStr = loc ? ` at line ${loc.start.line}, column ${loc.start.column + 1}` : "";
65
- throw new Error(`[formatjs] Cannot flatten message in file "${filename}"${locationStr}${id ? ` with id "${id}"` : ""}: ${e.message}\nMessage: ${defaultMessage}`);
66
- }
67
- }
68
- if (overrideIdFn) {
69
- id = overrideIdFn(id, defaultMessage, description, filename);
70
- } else if (!id && idInterpolationPattern && defaultMessage) {
71
- id = interpolateName({ resourcePath: filename }, idInterpolationPattern, { content: description ? `${defaultMessage}#${description}` : defaultMessage });
72
- }
73
- const descriptor = { id };
74
- if (description) {
75
- descriptor.description = description;
76
- }
77
- if (defaultMessage) {
78
- descriptor.defaultMessage = defaultMessage;
79
- }
80
- return descriptor;
81
- }
82
- function getICUMessageValue(messagePath, { isJSXSource = false } = {}, preserveWhitespace) {
83
- if (!messagePath) {
84
- return "";
85
- }
86
- let message = getMessageDescriptorValue(messagePath, true);
87
- if (!preserveWhitespace) {
88
- message = message.trim().replace(/\s+/gm, " ");
89
- }
90
- try {
91
- parse(message);
92
- } catch (parseError) {
93
- if (isJSXSource && messagePath.isLiteral() && message.indexOf("\\\\") >= 0) {
94
- throw messagePath.buildCodeFrameError("[React Intl] Message failed to parse. " + "It looks like `\\`s were used for escaping, " + "this won't work with JSX string literals. " + "Wrap with `{}`. " + "See: http://facebook.github.io/react/docs/jsx-gotchas.html");
95
- }
96
- throw messagePath.buildCodeFrameError("[React Intl] Message failed to parse. " + "See: https://formatjs.github.io/docs/core-concepts/icu-syntax" + `\n${parseError}`);
97
- }
98
- return message;
99
- }
100
- const EXTRACTED = Symbol("FormatJSExtracted");
101
- /**
102
- * Tag a node as extracted
103
- * Store this in the node itself so that multiple passes work. Specifically
104
- * if we remove `description` in the 1st pass, 2nd pass will fail since
105
- * it expect `description` to be there.
106
- * HACK: We store this in the node instance since this persists across
107
- * multiple plugin runs
108
- * @param path
109
- */
110
- export function tagAsExtracted(path) {
111
- path.node[EXTRACTED] = true;
112
- }
113
- /**
114
- * Check if a node was extracted
115
- * @param path
116
- */
117
- export function wasExtracted(path) {
118
- return !!path.node[EXTRACTED];
119
- }
120
- /**
121
- * Store a message in our global messages
122
- * @param messageDescriptor
123
- * @param path
124
- * @param opts
125
- * @param filename
126
- * @param messages
127
- */
128
- export function storeMessage({ id, description, defaultMessage }, path, { extractSourceLocation }, filename, messages) {
129
- if (!id && !defaultMessage) {
130
- throw path.buildCodeFrameError("[React Intl] Message Descriptors require an `id` or `defaultMessage`.");
131
- }
132
- let loc = {};
133
- if (extractSourceLocation) {
134
- loc = {
135
- file: filename,
136
- ...path.node.loc
137
- };
138
- }
139
- messages.push({
140
- id,
141
- description,
142
- defaultMessage,
143
- ...loc
144
- });
145
- }
@@ -1,5 +0,0 @@
1
- import { type PluginPass } from "@babel/core";
2
- import * as t from "@babel/types";
3
- import { type State } from "../types.js";
4
- import { type VisitNodeFunction } from "@babel/traverse";
5
- export declare const visitor: VisitNodeFunction<PluginPass & State, t.CallExpression>;
@@ -1,110 +0,0 @@
1
- import "@babel/core";
2
- import * as t from "@babel/types";
3
- import "../types.js";
4
- import "@babel/traverse";
5
- import { createMessageDescriptor, evaluateMessageDescriptor, wasExtracted, storeMessage, tagAsExtracted } from "../utils.js";
6
- import { parse } from "@formatjs/icu-messageformat-parser";
7
- function assertObjectExpression(path, callee) {
8
- if (!path || !path.isObjectExpression()) {
9
- throw path.buildCodeFrameError(`[React Intl] \`${callee.get("property").node.name}()\` must be called with an object expression with values that are React Intl Message Descriptors, also defined as object expressions.`);
10
- }
11
- }
12
- function isFormatMessageCall(callee, functionNames) {
13
- if (functionNames.find((name) => callee.isIdentifier({ name }))) {
14
- return true;
15
- }
16
- // GH #4471: Handle both MemberExpression and OptionalMemberExpression
17
- // (e.g., intl.formatMessage() and intl.formatMessage?.())
18
- if (callee.isMemberExpression() || callee.isOptionalMemberExpression()) {
19
- const property = callee.get("property");
20
- return !!functionNames.find((name) => property.isIdentifier({ name }));
21
- }
22
- return false;
23
- }
24
- function getMessagesObjectFromExpression(nodePath) {
25
- let currentPath = nodePath;
26
- while (t.isTSAsExpression(currentPath.node) || t.isTSTypeAssertion(currentPath.node) || t.isTypeCastExpression(currentPath.node)) {
27
- currentPath = currentPath.get("expression");
28
- }
29
- return currentPath;
30
- }
31
- export const visitor = function(path, { opts, file: { opts: { filename } } }) {
32
- const { overrideIdFn, idInterpolationPattern, removeDefaultMessage, ast, preserveWhitespace, flatten } = opts;
33
- if (wasExtracted(path)) {
34
- return;
35
- }
36
- const { messages, functionNames } = this;
37
- const callee = path.get("callee");
38
- const args = path.get("arguments");
39
- /**
40
- * Process MessageDescriptor
41
- * @param messageDescriptor Message Descriptor
42
- */
43
- function processMessageObject(messageDescriptor) {
44
- assertObjectExpression(messageDescriptor, callee);
45
- const properties = messageDescriptor.get("properties");
46
- const descriptorPath = createMessageDescriptor(properties.map((prop) => [prop.get("key"), prop.get("value")]));
47
- // If the message is already compiled, don't re-compile it
48
- if (descriptorPath.defaultMessage?.isArrayExpression()) {
49
- return;
50
- }
51
- // Evaluate the Message Descriptor values, then store it.
52
- const descriptor = evaluateMessageDescriptor(descriptorPath, false, filename || undefined, idInterpolationPattern, overrideIdFn, preserveWhitespace, flatten);
53
- storeMessage(descriptor, messageDescriptor, opts, filename || undefined, messages);
54
- const firstProp = properties[0];
55
- const defaultMessageProp = properties.find((prop) => {
56
- const keyProp = prop.get("key");
57
- return keyProp.isIdentifier({ name: "defaultMessage" }) || keyProp.isStringLiteral({ value: "defaultMessage" });
58
- });
59
- const idProp = properties.find((prop) => {
60
- const keyProp = prop.get("key");
61
- return keyProp.isIdentifier({ name: "id" }) || keyProp.isStringLiteral({ value: "id" });
62
- });
63
- // Insert ID potentially 1st before removing nodes
64
- if (idProp) {
65
- idProp.get("value").replaceWith(t.stringLiteral(descriptor.id));
66
- } else {
67
- firstProp.insertBefore(t.objectProperty(t.identifier("id"), t.stringLiteral(descriptor.id)));
68
- }
69
- // Remove description
70
- properties.find((prop) => {
71
- const keyProp = prop.get("key");
72
- return keyProp.isIdentifier({ name: "description" }) || keyProp.isStringLiteral({ value: "description" });
73
- })?.remove();
74
- // Pre-parse or remove defaultMessage
75
- if (defaultMessageProp) {
76
- if (removeDefaultMessage) {
77
- defaultMessageProp?.remove();
78
- } else if (descriptor.defaultMessage) {
79
- const valueProp = defaultMessageProp.get("value");
80
- if (ast) {
81
- valueProp.replaceWithSourceString(JSON.stringify(parse(descriptor.defaultMessage)));
82
- } else {
83
- valueProp.replaceWith(t.stringLiteral(descriptor.defaultMessage));
84
- }
85
- }
86
- }
87
- tagAsExtracted(path);
88
- }
89
- // Check that this is `defineMessages` call
90
- if (callee.isIdentifier({ name: "defineMessages" }) || callee.isIdentifier({ name: "defineMessage" })) {
91
- const firstArgument = args[0];
92
- const messagesObj = getMessagesObjectFromExpression(firstArgument);
93
- assertObjectExpression(messagesObj, callee);
94
- if (callee.isIdentifier({ name: "defineMessage" })) {
95
- processMessageObject(messagesObj);
96
- } else {
97
- const properties = messagesObj.get("properties");
98
- if (Array.isArray(properties)) {
99
- properties.map((prop) => prop.get("value")).forEach(processMessageObject);
100
- }
101
- }
102
- }
103
- // Check that this is `intl.formatMessage` call
104
- if (isFormatMessageCall(callee, functionNames)) {
105
- const messageDescriptor = args[0];
106
- if (messageDescriptor && messageDescriptor.isObjectExpression()) {
107
- processMessageObject(messageDescriptor);
108
- }
109
- }
110
- };
@@ -1,5 +0,0 @@
1
- import { type PluginPass } from "@babel/core";
2
- import { type State } from "../types.js";
3
- import * as t from "@babel/types";
4
- import { type VisitNodeFunction } from "@babel/traverse";
5
- export declare const visitor: VisitNodeFunction<PluginPass & State, t.JSXOpeningElement>;
@@ -1,74 +0,0 @@
1
- import "@babel/core";
2
- import "../types.js";
3
- import * as t from "@babel/types";
4
- import "@babel/traverse";
5
- import { parse } from "@formatjs/icu-messageformat-parser";
6
- import { createMessageDescriptor, evaluateMessageDescriptor, getMessageDescriptorKey, storeMessage, tagAsExtracted, wasExtracted } from "../utils.js";
7
- export const visitor = function(path, { opts, file: { opts: { filename } } }) {
8
- const { removeDefaultMessage, idInterpolationPattern, overrideIdFn, ast, preserveWhitespace, flatten } = opts;
9
- const { componentNames, messages } = this;
10
- if (wasExtracted(path)) {
11
- return;
12
- }
13
- const name = path.get("name");
14
- if (!componentNames.find((n) => name.isJSXIdentifier({ name: n }))) {
15
- return;
16
- }
17
- const attributes = path.get("attributes").filter((attr) => attr.isJSXAttribute());
18
- const descriptorPath = createMessageDescriptor(attributes.map((attr) => [attr.get("name"), attr.get("value")]));
19
- // In order for a default message to be extracted when
20
- // declaring a JSX element, it must be done with standard
21
- // `key=value` attributes. But it's completely valid to
22
- // write `<FormattedMessage {...descriptor} />`, because it will be
23
- // skipped here and extracted elsewhere. The descriptor will
24
- // be extracted only (storeMessage) if a `defaultMessage` prop.
25
- if (!descriptorPath.defaultMessage) {
26
- return;
27
- }
28
- // Evaluate the Message Descriptor values in a JSX
29
- // context, then store it.
30
- const descriptor = evaluateMessageDescriptor(descriptorPath, true, filename || undefined, idInterpolationPattern, overrideIdFn, preserveWhitespace, flatten);
31
- storeMessage(descriptor, path, opts, filename || undefined, messages);
32
- let idAttr;
33
- let descriptionAttr;
34
- let defaultMessageAttr;
35
- const firstAttr = attributes[0];
36
- for (const attr of attributes) {
37
- if (!attr.isJSXAttribute()) {
38
- continue;
39
- }
40
- switch (getMessageDescriptorKey(attr.get("name"))) {
41
- case "description":
42
- descriptionAttr = attr;
43
- break;
44
- case "defaultMessage":
45
- defaultMessageAttr = attr;
46
- break;
47
- case "id":
48
- idAttr = attr;
49
- break;
50
- }
51
- }
52
- // Insert ID before removing node to prevent null node insertBefore
53
- if (overrideIdFn || descriptor.id && idInterpolationPattern) {
54
- if (idAttr) {
55
- idAttr.get("value").replaceWith(t.stringLiteral(descriptor.id));
56
- } else if (firstAttr) {
57
- firstAttr.insertBefore(t.jsxAttribute(t.jsxIdentifier("id"), t.stringLiteral(descriptor.id)));
58
- }
59
- }
60
- if (descriptionAttr) {
61
- descriptionAttr.remove();
62
- }
63
- if (defaultMessageAttr) {
64
- if (removeDefaultMessage) {
65
- defaultMessageAttr.remove();
66
- } else if (ast && descriptor.defaultMessage) {
67
- defaultMessageAttr.get("value").replaceWith(t.jsxExpressionContainer(t.nullLiteral()));
68
- const valueAttr = defaultMessageAttr.get("value");
69
- valueAttr.get("expression").replaceWithSourceString(JSON.stringify(parse(descriptor.defaultMessage)));
70
- }
71
- }
72
- // Tag the AST node so we don't try to extract it twice.
73
- tagAsExtracted(path);
74
- };