@formatjs/unplugin 1.1.6 → 1.1.7

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/transform.js CHANGED
@@ -1,11 +1,12 @@
1
- import { parseSync, Visitor } from "oxc-parser";
1
+ import { Visitor, parseSync } from "oxc-parser";
2
2
  import MagicString from "magic-string";
3
3
  import { interpolateName } from "@formatjs/ts-transformer";
4
4
  import { parse } from "@formatjs/icu-messageformat-parser";
5
5
  import { hoistSelectors } from "@formatjs/icu-messageformat-parser/manipulator.js";
6
6
  import { printAST } from "@formatjs/icu-messageformat-parser/printer.js";
7
+ //#region packages/unplugin/transform.ts
7
8
  const DEFAULT_ID_INTERPOLATION_PATTERN = "[sha512:contenthash:base64:6]";
8
- export function transform(code, id, options) {
9
+ function transform(code, id, options) {
9
10
  const { overrideIdFn, idInterpolationPattern = DEFAULT_ID_INTERPOLATION_PATTERN, removeDefaultMessage = false, additionalComponentNames = [], additionalFunctionNames = [], ast: preParseAst = false, preserveWhitespace = false, flatten = true } = options;
10
11
  const componentNames = new Set(["FormattedMessage", ...additionalComponentNames]);
11
12
  const functionNames = new Set([
@@ -16,7 +17,6 @@ export function transform(code, id, options) {
16
17
  "defineMessages",
17
18
  ...additionalFunctionNames
18
19
  ]);
19
- // Compiled JSX runtime functions: _jsx(Component, props), React.createElement(Component, props)
20
20
  const jsxRuntimeFunctions = new Set([
21
21
  "jsx",
22
22
  "_jsx",
@@ -26,48 +26,41 @@ export function transform(code, id, options) {
26
26
  "_jsxDEV",
27
27
  "createElement"
28
28
  ]);
29
- const result = parseSync(id, code, { sourceType: "module" });
30
- const program = result.program;
29
+ const program = parseSync(id, code, { sourceType: "module" }).program;
31
30
  let s;
32
31
  function getStaticValue(node) {
33
- if (!node) return undefined;
32
+ if (!node) return void 0;
34
33
  if (node.type === "StringLiteral" || node.type === "Literal") {
35
34
  if (typeof node.value === "string") return node.value;
36
- return undefined;
35
+ return;
37
36
  }
38
37
  if (node.type === "TemplateLiteral") {
39
- if (node.quasis.length === 1 && node.expressions.length === 0) {
40
- return node.quasis[0].value.cooked ?? node.quasis[0].value.raw;
41
- }
42
- return undefined;
38
+ if (node.quasis.length === 1 && node.expressions.length === 0) return node.quasis[0].value.cooked ?? node.quasis[0].value.raw;
39
+ return;
43
40
  }
44
41
  if (node.type === "BinaryExpression" && node.operator === "+") {
45
42
  const left = getStaticValue(node.left);
46
43
  const right = getStaticValue(node.right);
47
- if (left !== undefined && right !== undefined) return left + right;
48
- return undefined;
44
+ if (left !== void 0 && right !== void 0) return left + right;
45
+ return;
49
46
  }
50
- return undefined;
51
47
  }
52
48
  function extractDescriptor(objNode) {
53
49
  const properties = objNode.properties;
54
50
  const descriptor = {};
55
51
  const locations = {};
56
- // Use the position right after `{` as a stable insertion point
57
52
  locations.insertionPoint = objNode.start + 1;
58
53
  for (const prop of properties) {
59
54
  if (prop.type !== "Property" && prop.type !== "ObjectProperty") continue;
60
55
  const key = prop.key;
61
56
  let name;
62
57
  if (key.type === "Identifier") name = key.name;
63
- else if (key.type === "StringLiteral" || key.type === "Literal") {
64
- name = key.value;
65
- }
58
+ else if (key.type === "StringLiteral" || key.type === "Literal") name = key.value;
66
59
  if (!name) continue;
67
60
  if (name !== "id" && name !== "defaultMessage" && name !== "description") continue;
68
61
  const keyName = name;
69
62
  const val = getStaticValue(prop.value);
70
- if (val === undefined) continue;
63
+ if (val === void 0) continue;
71
64
  descriptor[keyName] = val;
72
65
  locations[keyName] = {
73
66
  start: prop.value.start,
@@ -75,7 +68,7 @@ export function transform(code, id, options) {
75
68
  value: val
76
69
  };
77
70
  }
78
- if (!descriptor.defaultMessage && !descriptor.id) return undefined;
71
+ if (!descriptor.defaultMessage && !descriptor.id) return void 0;
79
72
  return {
80
73
  descriptor,
81
74
  locations
@@ -85,8 +78,6 @@ export function transform(code, id, options) {
85
78
  const attributes = elementNode.attributes || [];
86
79
  const descriptor = {};
87
80
  const locations = {};
88
- // Always insert after the element name so the id survives even when the
89
- // first attribute (e.g. description) is removed.
90
81
  locations.insertionPoint = elementNode.name.end;
91
82
  for (const attr of attributes) {
92
83
  if (attr.type !== "JSXAttribute") continue;
@@ -98,12 +89,9 @@ export function transform(code, id, options) {
98
89
  let val;
99
90
  const valueNode = attr.value;
100
91
  if (!valueNode) continue;
101
- if (valueNode.type === "StringLiteral" || valueNode.type === "Literal") {
102
- val = valueNode.value;
103
- } else if (valueNode.type === "JSXExpressionContainer") {
104
- val = getStaticValue(valueNode.expression);
105
- }
106
- if (val === undefined) continue;
92
+ if (valueNode.type === "StringLiteral" || valueNode.type === "Literal") val = valueNode.value;
93
+ else if (valueNode.type === "JSXExpressionContainer") val = getStaticValue(valueNode.expression);
94
+ if (val === void 0) continue;
107
95
  descriptor[keyName] = val;
108
96
  locations[keyName] = {
109
97
  start: attr.value.start,
@@ -111,7 +99,7 @@ export function transform(code, id, options) {
111
99
  value: val
112
100
  };
113
101
  }
114
- if (!descriptor.defaultMessage && !descriptor.id) return undefined;
102
+ if (!descriptor.defaultMessage && !descriptor.id) return void 0;
115
103
  return {
116
104
  descriptor,
117
105
  locations
@@ -120,99 +108,54 @@ export function transform(code, id, options) {
120
108
  function processDescriptor(descriptor, locations, isJSX) {
121
109
  let { defaultMessage, description } = descriptor;
122
110
  let existingId = descriptor.id;
123
- // Normalize whitespace on defaultMessage
124
- if (defaultMessage && !preserveWhitespace) {
125
- defaultMessage = defaultMessage.trim().replace(/\s+/gm, " ");
126
- }
127
- // Apply flatten transformation (hoist selectors + normalize ICU format via printAST)
128
- // This must happen before ID generation so the ID matches formatjs extract and babel-plugin-formatjs
129
- if (flatten && defaultMessage) {
130
- try {
131
- defaultMessage = printAST(hoistSelectors(parse(defaultMessage)));
132
- } catch {}
133
- }
134
- // Generate ID
111
+ if (defaultMessage && !preserveWhitespace) defaultMessage = defaultMessage.trim().replace(/\s+/gm, " ");
112
+ if (flatten && defaultMessage) try {
113
+ defaultMessage = printAST(hoistSelectors(parse(defaultMessage)));
114
+ } catch {}
135
115
  let newId = existingId;
136
- if (overrideIdFn) {
137
- newId = overrideIdFn(existingId, defaultMessage, description, id);
138
- } else if (!existingId && idInterpolationPattern && defaultMessage) {
139
- newId = interpolateName({ resourcePath: id }, idInterpolationPattern, { content: description ? `${defaultMessage}#${description}` : defaultMessage });
140
- }
116
+ if (overrideIdFn) newId = overrideIdFn(existingId, defaultMessage, description, id);
117
+ else if (!existingId && idInterpolationPattern && defaultMessage) newId = interpolateName({ resourcePath: id }, idInterpolationPattern, { content: description ? `${defaultMessage}#${description}` : defaultMessage });
141
118
  if (!newId && !defaultMessage) return;
142
119
  if (!s) s = new MagicString(code);
143
- // Insert/update id
144
120
  if (newId) {
145
- if (locations.id) {
146
- // Replace existing id value
147
- s.overwrite(locations.id.start, locations.id.end, JSON.stringify(newId));
148
- } else if (locations.insertionPoint != null) {
149
- // Insert id at a stable position (after `{` for objects, before first attr for JSX)
150
- if (isJSX) {
151
- s.appendLeft(locations.insertionPoint, ` id=${JSON.stringify(newId)}`);
152
- } else {
153
- s.appendRight(locations.insertionPoint, `id: ${JSON.stringify(newId)}, `);
154
- }
155
- }
156
- }
157
- // Remove description
158
- if (locations.description) {
159
- if (isJSX) {
160
- // For JSX, we need to remove the whole attribute including the key
161
- // Find the JSX attribute that contains this value
162
- removeJSXAttribute(locations.description);
163
- } else {
164
- removeObjectProperty(locations.description);
165
- }
121
+ if (locations.id) s.overwrite(locations.id.start, locations.id.end, JSON.stringify(newId));
122
+ else if (locations.insertionPoint != null) if (isJSX) s.appendLeft(locations.insertionPoint, ` id=${JSON.stringify(newId)}`);
123
+ else s.appendRight(locations.insertionPoint, `id: ${JSON.stringify(newId)}, `);
166
124
  }
167
- // Handle defaultMessage
125
+ if (locations.description) if (isJSX) removeJSXAttribute(locations.description);
126
+ else removeObjectProperty(locations.description);
168
127
  if (locations.defaultMessage) {
169
- if (removeDefaultMessage) {
170
- if (isJSX) {
171
- removeJSXAttribute(locations.defaultMessage);
172
- } else {
173
- removeObjectProperty(locations.defaultMessage);
174
- }
175
- } else if (defaultMessage) {
128
+ if (removeDefaultMessage) if (isJSX) removeJSXAttribute(locations.defaultMessage);
129
+ else removeObjectProperty(locations.defaultMessage);
130
+ else if (defaultMessage) {
176
131
  if (preParseAst) {
177
132
  const parsed = parse(defaultMessage);
178
133
  const jsonStr = JSON.stringify(parsed);
179
134
  s.overwrite(locations.defaultMessage.start, locations.defaultMessage.end, isJSX ? `{${jsonStr}}` : jsonStr);
180
- } else if (defaultMessage !== locations.defaultMessage.value) {
181
- // Whitespace was normalized, update the value
182
- s.overwrite(locations.defaultMessage.start, locations.defaultMessage.end, JSON.stringify(defaultMessage));
183
- }
135
+ } else if (defaultMessage !== locations.defaultMessage.value) s.overwrite(locations.defaultMessage.start, locations.defaultMessage.end, JSON.stringify(defaultMessage));
184
136
  }
185
137
  }
186
138
  }
187
139
  function removeObjectProperty(loc) {
188
140
  if (!s) return;
189
- // Find the property boundaries: walk back to find key start, walk forward past trailing comma
190
141
  let propStart = loc.start;
191
- // Walk backward past value, colon, key, and any whitespace
192
142
  let i = loc.start - 1;
193
143
  while (i >= 0 && (code[i] === " " || code[i] === " " || code[i] === "\n" || code[i] === "\r")) i--;
194
- // Skip colon
195
144
  if (i >= 0 && code[i] === ":") i--;
196
145
  while (i >= 0 && (code[i] === " " || code[i] === " " || code[i] === "\n" || code[i] === "\r")) i--;
197
- // Walk backward through key (identifier or string literal)
198
146
  if (i >= 0 && (code[i] === "\"" || code[i] === "'")) {
199
147
  const quote = code[i];
200
148
  i--;
201
149
  while (i >= 0 && code[i] !== quote) i--;
202
150
  if (i >= 0) i--;
203
- } else {
204
- while (i >= 0 && /[a-zA-Z0-9_$]/.test(code[i])) i--;
205
- }
151
+ } else while (i >= 0 && /[a-zA-Z0-9_$]/.test(code[i])) i--;
206
152
  propStart = i + 1;
207
153
  let propEnd = loc.end;
208
- // Walk forward past trailing comma and whitespace
209
154
  let j = loc.end;
210
155
  while (j < code.length && (code[j] === " " || code[j] === " ")) j++;
211
156
  if (j < code.length && code[j] === ",") {
212
157
  j++;
213
- // Also skip whitespace after comma
214
158
  while (j < code.length && (code[j] === " " || code[j] === " ")) j++;
215
- // Skip one newline
216
159
  if (j < code.length && code[j] === "\n") j++;
217
160
  else if (j < code.length && code[j] === "\r") {
218
161
  j++;
@@ -220,31 +163,20 @@ export function transform(code, id, options) {
220
163
  }
221
164
  }
222
165
  propEnd = j;
223
- // Also remove leading whitespace/newline
224
166
  let k = propStart - 1;
225
167
  while (k >= 0 && (code[k] === " " || code[k] === " ")) k--;
226
- if (k >= 0 && (code[k] === "\n" || code[k] === ",")) {
227
- propStart = k + 1;
228
- }
168
+ if (k >= 0 && (code[k] === "\n" || code[k] === ",")) propStart = k + 1;
229
169
  s.remove(propStart, propEnd);
230
170
  }
231
171
  function removeJSXAttribute(loc) {
232
172
  if (!s) return;
233
- // Walk backward from the value to find attribute name
234
173
  let i = loc.start - 1;
235
- // Skip the `=`
236
174
  if (i >= 0 && code[i] === "=") i--;
237
- // Walk backward through attribute name
238
175
  while (i >= 0 && /[a-zA-Z0-9_$]/.test(code[i])) i--;
239
176
  const attrStart = i + 1;
240
177
  const attrEnd = loc.end;
241
- // Remove leading whitespace/newline before attribute name.
242
- // We intentionally do NOT consume trailing whitespace so that when attributes
243
- // are on the same line the space before the next attribute is preserved.
244
- // e.g. `<FM description="x" defaultMessage="y" />` → `<FM defaultMessage="y" />`
245
178
  let k = attrStart - 1;
246
179
  while (k >= 0 && (code[k] === " " || code[k] === " ")) k--;
247
- // Also include the preceding newline for multi-line JSX so the whole line is removed.
248
180
  if (k >= 0 && code[k] === "\n") k--;
249
181
  if (k >= 0 && code[k] === "\r") k--;
250
182
  const removeStart = k + 1;
@@ -255,66 +187,54 @@ export function transform(code, id, options) {
255
187
  if (node.type === "MemberExpression" || node.type === "OptionalMemberExpression") {
256
188
  if (node.property?.type === "Identifier") return node.property.name;
257
189
  }
258
- return undefined;
259
190
  }
260
191
  function handleCallExpression(node) {
261
192
  const calleeName = getCalleeName(node.callee);
262
- // Handle compiled JSX: _jsx(FormattedMessage, { id, description, defaultMessage })
263
193
  if (calleeName && jsxRuntimeFunctions.has(calleeName)) {
264
194
  const firstArg = node.arguments?.[0];
265
- const componentName = firstArg?.type === "Identifier" ? firstArg.name : undefined;
195
+ const componentName = firstArg?.type === "Identifier" ? firstArg.name : void 0;
266
196
  if (componentName && componentNames.has(componentName)) {
267
197
  const propsArg = node.arguments?.[1];
268
198
  if (propsArg?.type === "ObjectExpression") {
269
199
  const result = extractDescriptor(propsArg);
270
- if (result) {
271
- processDescriptor(result.descriptor, result.locations, false);
272
- }
200
+ if (result) processDescriptor(result.descriptor, result.locations, false);
273
201
  }
274
202
  }
275
203
  }
276
- if (calleeName && functionNames.has(calleeName)) {
277
- if (calleeName === "defineMessages") {
278
- // Process each value in the object
279
- const arg = node.arguments?.[0];
280
- if (arg?.type === "ObjectExpression") {
281
- for (const prop of arg.properties) {
282
- if (prop.type === "Property" && prop.value?.type === "ObjectExpression") {
283
- const result = extractDescriptor(prop.value);
284
- if (result) {
285
- processDescriptor(result.descriptor, result.locations, false);
286
- }
287
- }
288
- }
289
- }
290
- } else {
291
- const arg = node.arguments?.[0];
292
- if (arg?.type === "ObjectExpression") {
293
- const result = extractDescriptor(arg);
294
- if (result) {
295
- processDescriptor(result.descriptor, result.locations, false);
296
- }
204
+ if (calleeName && functionNames.has(calleeName)) if (calleeName === "defineMessages") {
205
+ const arg = node.arguments?.[0];
206
+ if (arg?.type === "ObjectExpression") {
207
+ for (const prop of arg.properties) if (prop.type === "Property" && prop.value?.type === "ObjectExpression") {
208
+ const result = extractDescriptor(prop.value);
209
+ if (result) processDescriptor(result.descriptor, result.locations, false);
297
210
  }
298
211
  }
212
+ } else {
213
+ const arg = node.arguments?.[0];
214
+ if (arg?.type === "ObjectExpression") {
215
+ const result = extractDescriptor(arg);
216
+ if (result) processDescriptor(result.descriptor, result.locations, false);
217
+ }
299
218
  }
300
219
  }
301
220
  function handleJSXOpeningElement(node) {
302
- const name = node.name?.type === "JSXIdentifier" ? node.name.name : undefined;
221
+ const name = node.name?.type === "JSXIdentifier" ? node.name.name : void 0;
303
222
  if (name && componentNames.has(name)) {
304
223
  const result = extractJSXDescriptor(node);
305
- if (result) {
306
- processDescriptor(result.descriptor, result.locations, true);
307
- }
224
+ if (result) processDescriptor(result.descriptor, result.locations, true);
308
225
  }
309
226
  }
310
- const visitor = new Visitor({
227
+ new Visitor({
311
228
  CallExpression: handleCallExpression,
312
229
  JSXOpeningElement: handleJSXOpeningElement
313
- });
314
- visitor.visit(program);
315
- if (!s) return undefined;
230
+ }).visit(program);
231
+ if (!s) return void 0;
316
232
  return {
317
233
  code: s.toString(),
318
234
  map: s.generateMap({ hires: "boundary" })
319
235
  };
320
236
  }
237
+ //#endregion
238
+ export { transform };
239
+
240
+ //# sourceMappingURL=transform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transform.js","names":[],"sources":["../transform.ts"],"sourcesContent":["import {parseSync, Visitor} from 'oxc-parser'\nimport type {CallExpression, JSXOpeningElement} from 'oxc-parser'\nimport MagicString from 'magic-string'\nimport {interpolateName} from '@formatjs/ts-transformer'\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'\n\nexport interface Options {\n overrideIdFn?: (\n id: string | undefined,\n defaultMessage: string | undefined,\n description: string | undefined,\n filePath: string\n ) => string\n idInterpolationPattern?: string\n removeDefaultMessage?: boolean\n additionalComponentNames?: string[]\n additionalFunctionNames?: string[]\n ast?: boolean\n preserveWhitespace?: boolean\n flatten?: boolean\n}\n\nconst DEFAULT_ID_INTERPOLATION_PATTERN = '[sha512:contenthash:base64:6]'\n\ninterface MessageDescriptor {\n id?: string\n defaultMessage?: string\n description?: string\n}\n\ninterface DescriptorLocation {\n id?: {start: number; end: number; value: string}\n defaultMessage?: {start: number; end: number; value: string}\n description?: {start: number; end: number; value: string}\n /** Position right after the opening `{` or element name — stable insertion point for `id` */\n insertionPoint?: number\n}\n\nexport function transform(\n code: string,\n id: string,\n options: Options\n): {code: string; map: ReturnType<MagicString['generateMap']>} | undefined {\n const {\n overrideIdFn,\n idInterpolationPattern = DEFAULT_ID_INTERPOLATION_PATTERN,\n removeDefaultMessage = false,\n additionalComponentNames = [],\n additionalFunctionNames = [],\n ast: preParseAst = false,\n preserveWhitespace = false,\n flatten = true,\n } = options\n\n const componentNames = new Set([\n 'FormattedMessage',\n ...additionalComponentNames,\n ])\n const functionNames = new Set([\n 'formatMessage',\n '$t',\n '$formatMessage',\n 'defineMessage',\n 'defineMessages',\n ...additionalFunctionNames,\n ])\n // Compiled JSX runtime functions: _jsx(Component, props), React.createElement(Component, props)\n const jsxRuntimeFunctions = new Set([\n 'jsx',\n '_jsx',\n 'jsxs',\n '_jsxs',\n 'jsxDEV',\n '_jsxDEV',\n 'createElement',\n ])\n\n const result = parseSync(id, code, {sourceType: 'module'})\n const program = result.program\n\n let s: MagicString | undefined\n\n function getStaticValue(node: any): string | undefined {\n if (!node) return undefined\n if (node.type === 'StringLiteral' || node.type === 'Literal') {\n if (typeof node.value === 'string') return node.value\n return undefined\n }\n if (node.type === 'TemplateLiteral') {\n if (node.quasis.length === 1 && node.expressions.length === 0) {\n return node.quasis[0].value.cooked ?? node.quasis[0].value.raw\n }\n return undefined\n }\n if (node.type === 'BinaryExpression' && node.operator === '+') {\n const left = getStaticValue(node.left)\n const right = getStaticValue(node.right)\n if (left !== undefined && right !== undefined) return left + right\n return undefined\n }\n return undefined\n }\n\n function extractDescriptor(\n objNode: any\n ):\n | {descriptor: MessageDescriptor; locations: DescriptorLocation}\n | undefined {\n const properties = objNode.properties\n const descriptor: MessageDescriptor = {}\n const locations: DescriptorLocation = {}\n\n // Use the position right after `{` as a stable insertion point\n locations.insertionPoint = objNode.start + 1\n\n for (const prop of properties) {\n if (prop.type !== 'Property' && prop.type !== 'ObjectProperty') continue\n const key = prop.key\n let name: string | undefined\n if (key.type === 'Identifier') name = key.name\n else if (key.type === 'StringLiteral' || key.type === 'Literal') {\n name = key.value as string\n }\n if (!name) continue\n if (name !== 'id' && name !== 'defaultMessage' && name !== 'description')\n continue\n const keyName: keyof MessageDescriptor = name\n\n const val = getStaticValue(prop.value)\n if (val === undefined) continue\n\n descriptor[keyName] = val\n locations[keyName] = {\n start: prop.value.start,\n end: prop.value.end,\n value: val,\n }\n }\n\n if (!descriptor.defaultMessage && !descriptor.id) return undefined\n return {descriptor, locations}\n }\n\n function extractJSXDescriptor(\n elementNode: any\n ):\n | {descriptor: MessageDescriptor; locations: DescriptorLocation}\n | undefined {\n const attributes = elementNode.attributes || []\n const descriptor: MessageDescriptor = {}\n const locations: DescriptorLocation = {}\n\n // Always insert after the element name so the id survives even when the\n // first attribute (e.g. description) is removed.\n locations.insertionPoint = elementNode.name.end\n\n for (const attr of attributes) {\n if (attr.type !== 'JSXAttribute') continue\n const attrName = attr.name\n if (!attrName || attrName.type !== 'JSXIdentifier') continue\n const n = attrName.name\n if (n !== 'id' && n !== 'defaultMessage' && n !== 'description') continue\n const keyName: keyof MessageDescriptor = n\n\n let val: string | undefined\n const valueNode = attr.value\n if (!valueNode) continue\n\n if (valueNode.type === 'StringLiteral' || valueNode.type === 'Literal') {\n val = valueNode.value as string\n } else if (valueNode.type === 'JSXExpressionContainer') {\n val = getStaticValue(valueNode.expression)\n }\n\n if (val === undefined) continue\n\n descriptor[keyName] = val\n locations[keyName] = {\n start: attr.value.start,\n end: attr.value.end,\n value: val,\n }\n }\n\n if (!descriptor.defaultMessage && !descriptor.id) return undefined\n return {descriptor, locations}\n }\n\n function processDescriptor(\n descriptor: MessageDescriptor,\n locations: DescriptorLocation,\n isJSX: boolean\n ): void {\n let {defaultMessage, description} = descriptor\n let existingId = descriptor.id\n\n // Normalize whitespace on defaultMessage\n if (defaultMessage && !preserveWhitespace) {\n defaultMessage = defaultMessage.trim().replace(/\\s+/gm, ' ')\n }\n\n // Apply flatten transformation (hoist selectors + normalize ICU format via printAST)\n // This must happen before ID generation so the ID matches formatjs extract and babel-plugin-formatjs\n if (flatten && defaultMessage) {\n try {\n defaultMessage = printAST(hoistSelectors(parse(defaultMessage)))\n } catch {\n // If parsing fails, keep the original message\n }\n }\n\n // Generate ID\n let newId = existingId\n if (overrideIdFn) {\n newId = overrideIdFn(existingId, defaultMessage, description, id)\n } else if (!existingId && idInterpolationPattern && defaultMessage) {\n newId = interpolateName(\n {resourcePath: id} as any,\n idInterpolationPattern,\n {\n content: description\n ? `${defaultMessage}#${description}`\n : defaultMessage,\n }\n )\n }\n\n if (!newId && !defaultMessage) return\n\n if (!s) s = new MagicString(code)\n\n // Insert/update id\n if (newId) {\n if (locations.id) {\n // Replace existing id value\n s.overwrite(locations.id.start, locations.id.end, JSON.stringify(newId))\n } else if (locations.insertionPoint != null) {\n // Insert id at a stable position (after `{` for objects, before first attr for JSX)\n if (isJSX) {\n s.appendLeft(locations.insertionPoint, ` id=${JSON.stringify(newId)}`)\n } else {\n s.appendRight(\n locations.insertionPoint,\n `id: ${JSON.stringify(newId)}, `\n )\n }\n }\n }\n\n // Remove description\n if (locations.description) {\n if (isJSX) {\n // For JSX, we need to remove the whole attribute including the key\n // Find the JSX attribute that contains this value\n removeJSXAttribute(locations.description)\n } else {\n removeObjectProperty(locations.description)\n }\n }\n\n // Handle defaultMessage\n if (locations.defaultMessage) {\n if (removeDefaultMessage) {\n if (isJSX) {\n removeJSXAttribute(locations.defaultMessage)\n } else {\n removeObjectProperty(locations.defaultMessage)\n }\n } else if (defaultMessage) {\n if (preParseAst) {\n const parsed = parse(defaultMessage)\n const jsonStr = JSON.stringify(parsed)\n s.overwrite(\n locations.defaultMessage.start,\n locations.defaultMessage.end,\n isJSX ? `{${jsonStr}}` : jsonStr\n )\n } else if (defaultMessage !== locations.defaultMessage.value) {\n // Whitespace was normalized, update the value\n s.overwrite(\n locations.defaultMessage.start,\n locations.defaultMessage.end,\n JSON.stringify(defaultMessage)\n )\n }\n }\n }\n }\n\n function removeObjectProperty(loc: {start: number; end: number}): void {\n if (!s) return\n // Find the property boundaries: walk back to find key start, walk forward past trailing comma\n let propStart = loc.start\n // Walk backward past value, colon, key, and any whitespace\n let i = loc.start - 1\n while (\n i >= 0 &&\n (code[i] === ' ' ||\n code[i] === '\\t' ||\n code[i] === '\\n' ||\n code[i] === '\\r')\n )\n i--\n // Skip colon\n if (i >= 0 && code[i] === ':') i--\n while (\n i >= 0 &&\n (code[i] === ' ' ||\n code[i] === '\\t' ||\n code[i] === '\\n' ||\n code[i] === '\\r')\n )\n i--\n // Walk backward through key (identifier or string literal)\n if (i >= 0 && (code[i] === '\"' || code[i] === \"'\")) {\n const quote = code[i]\n i--\n while (i >= 0 && code[i] !== quote) i--\n if (i >= 0) i-- // skip opening quote\n } else {\n while (i >= 0 && /[a-zA-Z0-9_$]/.test(code[i])) i--\n }\n propStart = i + 1\n\n let propEnd = loc.end\n // Walk forward past trailing comma and whitespace\n let j = loc.end\n while (j < code.length && (code[j] === ' ' || code[j] === '\\t')) j++\n if (j < code.length && code[j] === ',') {\n j++\n // Also skip whitespace after comma\n while (j < code.length && (code[j] === ' ' || code[j] === '\\t')) j++\n // Skip one newline\n if (j < code.length && code[j] === '\\n') j++\n else if (j < code.length && code[j] === '\\r') {\n j++\n if (j < code.length && code[j] === '\\n') j++\n }\n }\n propEnd = j\n\n // Also remove leading whitespace/newline\n let k = propStart - 1\n while (k >= 0 && (code[k] === ' ' || code[k] === '\\t')) k--\n if (k >= 0 && (code[k] === '\\n' || code[k] === ',')) {\n propStart = k + 1\n }\n\n s.remove(propStart, propEnd)\n }\n\n function removeJSXAttribute(loc: {start: number; end: number}): void {\n if (!s) return\n // Walk backward from the value to find attribute name\n let i = loc.start - 1\n // Skip the `=`\n if (i >= 0 && code[i] === '=') i--\n // Walk backward through attribute name\n while (i >= 0 && /[a-zA-Z0-9_$]/.test(code[i])) i--\n const attrStart = i + 1\n\n const attrEnd = loc.end\n\n // Remove leading whitespace/newline before attribute name.\n // We intentionally do NOT consume trailing whitespace so that when attributes\n // are on the same line the space before the next attribute is preserved.\n // e.g. `<FM description=\"x\" defaultMessage=\"y\" />` → `<FM defaultMessage=\"y\" />`\n let k = attrStart - 1\n while (k >= 0 && (code[k] === ' ' || code[k] === '\\t')) k--\n // Also include the preceding newline for multi-line JSX so the whole line is removed.\n if (k >= 0 && code[k] === '\\n') k--\n if (k >= 0 && code[k] === '\\r') k--\n const removeStart = k + 1\n\n s.remove(removeStart, attrEnd)\n }\n\n function getCalleeName(node: any): string | undefined {\n if (node.type === 'Identifier') return node.name\n if (\n node.type === 'MemberExpression' ||\n node.type === 'OptionalMemberExpression'\n ) {\n if (node.property?.type === 'Identifier') return node.property.name\n }\n return undefined\n }\n\n function handleCallExpression(node: CallExpression): void {\n const calleeName = getCalleeName(node.callee)\n\n // Handle compiled JSX: _jsx(FormattedMessage, { id, description, defaultMessage })\n if (calleeName && jsxRuntimeFunctions.has(calleeName)) {\n const firstArg = node.arguments?.[0]\n const componentName =\n firstArg?.type === 'Identifier' ? firstArg.name : undefined\n if (componentName && componentNames.has(componentName)) {\n const propsArg = node.arguments?.[1]\n if (propsArg?.type === 'ObjectExpression') {\n const result = extractDescriptor(propsArg)\n if (result) {\n processDescriptor(result.descriptor, result.locations, false)\n }\n }\n }\n }\n\n if (calleeName && functionNames.has(calleeName)) {\n if (calleeName === 'defineMessages') {\n // Process each value in the object\n const arg = node.arguments?.[0]\n if (arg?.type === 'ObjectExpression') {\n for (const prop of arg.properties) {\n if (\n prop.type === 'Property' &&\n prop.value?.type === 'ObjectExpression'\n ) {\n const result = extractDescriptor(prop.value)\n if (result) {\n processDescriptor(result.descriptor, result.locations, false)\n }\n }\n }\n }\n } else {\n const arg = node.arguments?.[0]\n if (arg?.type === 'ObjectExpression') {\n const result = extractDescriptor(arg)\n if (result) {\n processDescriptor(result.descriptor, result.locations, false)\n }\n }\n }\n }\n }\n\n function handleJSXOpeningElement(node: JSXOpeningElement): void {\n const name =\n node.name?.type === 'JSXIdentifier' ? node.name.name : undefined\n if (name && componentNames.has(name)) {\n const result = extractJSXDescriptor(node)\n if (result) {\n processDescriptor(result.descriptor, result.locations, true)\n }\n }\n }\n\n const visitor = new Visitor({\n CallExpression: handleCallExpression,\n JSXOpeningElement: handleJSXOpeningElement,\n })\n\n visitor.visit(program)\n\n if (!s) return undefined\n\n return {\n code: s.toString(),\n map: s.generateMap({hires: 'boundary'}),\n }\n}\n"],"mappings":"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"}
package/vite.d.ts CHANGED
@@ -1,5 +1,20 @@
1
- import { type UnpluginInstance } from "unplugin";
2
- import type { Options } from "./transform.js";
1
+ import { UnpluginInstance } from "unplugin";
2
+ import MagicString from "magic-string";
3
+
4
+ //#region packages/unplugin/transform.d.ts
5
+ interface Options {
6
+ overrideIdFn?: (id: string | undefined, defaultMessage: string | undefined, description: string | undefined, filePath: string) => string;
7
+ idInterpolationPattern?: string;
8
+ removeDefaultMessage?: boolean;
9
+ additionalComponentNames?: string[];
10
+ additionalFunctionNames?: string[];
11
+ ast?: boolean;
12
+ preserveWhitespace?: boolean;
13
+ flatten?: boolean;
14
+ }
15
+ //#endregion
16
+ //#region packages/unplugin/vite.d.ts
3
17
  declare const plugin: UnpluginInstance<Options | undefined>["vite"];
4
- export default plugin;
5
- export type { Options } from "./transform.js";
18
+ //#endregion
19
+ export { type Options, plugin as default };
20
+ //# sourceMappingURL=vite.d.ts.map