@intlayer/babel 9.5.0 → 9.5.2

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.
@@ -5,6 +5,13 @@ let _intlayer_config_callers = require("@intlayer/config/callers");
5
5
 
6
6
  //#region src/compat/optimizePass.ts
7
7
  /**
8
+ * Factories a JSX element compiles into — `jsx(Trans, props)` /
9
+ * `_jsxs(Trans, props)` / `jsxRuntime.jsxDEV(Trans, props)` for the automatic
10
+ * runtime, `React.createElement(Trans, props)` for the classic one. The
11
+ * transform aliases the import (`_jsx`), hence the optional underscores.
12
+ */
13
+ const COMPILED_JSX_FACTORY_PATTERN = /^_*(jsxs?|jsxDEV|createElement)$/;
14
+ /**
8
15
  * Rewritable slice of a descriptor registry, cached by array reference.
9
16
  *
10
17
  * Babel reuses one plugin-options object for every file of a build, so the
@@ -37,6 +44,44 @@ const getCallerIndex = (rewritableCallers) => {
37
44
  return index;
38
45
  };
39
46
  /**
47
+ * Prop through which a bound compat JSX element (`<Trans>`) receives its
48
+ * dictionary — the static dictionary, or a `[loader, key]` pair in dynamic
49
+ * mode. The compat package's replacement component reads it.
50
+ */
51
+ const JSX_DICTIONARY_ATTRIBUTE = "dictionary";
52
+ /**
53
+ * Whether a descriptor's call sites carry the message id themselves, with the
54
+ * dictionary named by the id's first dot-segment (lingui's `_` / `t` /
55
+ * `<Trans>`, react-intl's `formatMessage`). These are the sites a root-scope
56
+ * binding reads its dictionaries from.
57
+ */
58
+ const isMessageIdCaller = (descriptor) => descriptor.translationFunction === "self" && descriptor.namespaceSources.some((source) => source.from === "path-first-segment");
59
+ /**
60
+ * `library` → its message-id callers, cached by registry reference.
61
+ *
62
+ * Read from the full registry rather than its rewritable slice: the id
63
+ * callers are matched as methods or JSX and are never rewritten themselves —
64
+ * they only name the dictionaries a root-scope call in the same file binds.
65
+ */
66
+ const messageIdCallersCache = /* @__PURE__ */ new WeakMap();
67
+ const getMessageIdCallersByLibrary = (compatCallers) => {
68
+ let byLibrary = messageIdCallersCache.get(compatCallers);
69
+ if (!byLibrary) {
70
+ byLibrary = /* @__PURE__ */ new Map();
71
+ for (const descriptor of compatCallers) {
72
+ if (!isMessageIdCaller(descriptor)) continue;
73
+ let descriptors = byLibrary.get(descriptor.library);
74
+ if (!descriptors) {
75
+ descriptors = [];
76
+ byLibrary.set(descriptor.library, descriptors);
77
+ }
78
+ descriptors.push(descriptor);
79
+ }
80
+ messageIdCallersCache.set(compatCallers, byLibrary);
81
+ }
82
+ return byLibrary;
83
+ };
84
+ /**
40
85
  * Builds the compat optimize pass for one source file, or `null` when no
41
86
  * compat caller was injected.
42
87
  *
@@ -49,10 +94,39 @@ const createCompatOptimizePass = (babelTypes, compatCallers, imports, buildModes
49
94
  const rewritableCallers = getCachedRewritableCallers(compatCallers);
50
95
  if (rewritableCallers.length === 0) return null;
51
96
  const callerIndex = getCallerIndex(rewritableCallers);
97
+ const messageIdCallersByLibrary = getMessageIdCallersByLibrary(compatCallers);
52
98
  const { importMode, dictionaryModeMap } = buildModes;
53
99
  /** Local alias → the descriptor it was imported as. */
54
100
  const callersByLocalName = /* @__PURE__ */ new Map();
55
101
  /**
102
+ * Root-scope binding per library, filled from the message-id sites the
103
+ * traversal hands over. A library is present only once a root-scope caller
104
+ * of it was imported, so the sites of other libraries are never read.
105
+ */
106
+ const rootScopeBindings = /* @__PURE__ */ new Map();
107
+ /**
108
+ * Local names a bare message call may use per library: message callers
109
+ * imported from the library (`import { t } from '@lingui/macro'`) and
110
+ * names destructured from a root-scope call's result
111
+ * (`const { _ } = useLingui()`). A bare `t('…')` bound to anything else —
112
+ * say a local wrapper — is not a message-id site, mirroring the usage
113
+ * analyser; method calls (`i18n._('…')`) match by method name alone.
114
+ */
115
+ const bareMessageCallerNames = /* @__PURE__ */ new Map();
116
+ /**
117
+ * Bare calls met during the traversal, decided once every binding name is
118
+ * known — a destructuring may sit below the call sites in source order.
119
+ */
120
+ const pendingBareCalls = [];
121
+ const addBareMessageCallerName = (library, name) => {
122
+ let names = bareMessageCallerNames.get(library);
123
+ if (!names) {
124
+ names = /* @__PURE__ */ new Set();
125
+ bareMessageCallerNames.set(library, names);
126
+ }
127
+ names.add(name);
128
+ };
129
+ /**
56
130
  * File-level decision: one import specifier serves every call in the file, so
57
131
  * a global dynamic/fetch mode — or any per-dictionary override reached from
58
132
  * this file — flips all rewritten compat calls to the dynamic helper.
@@ -67,15 +141,178 @@ const createCompatOptimizePass = (babelTypes, compatCallers, imports, buildModes
67
141
  if (isDynamicMode(override)) return override;
68
142
  return isDynamicMode(importMode) ? importMode : "dynamic";
69
143
  };
144
+ /**
145
+ * Whether a call binds through the root-scope path: its descriptor allows
146
+ * it, the library has id callers to read from, and the call itself names
147
+ * no dictionary (`useLingui()`).
148
+ */
149
+ const isRootScopeCall = (descriptor, callArguments) => descriptor.allowRootScope === true && messageIdCallersByLibrary.has(descriptor.library) && require_compat_namespaceResolution.resolveNamespaceForRewrite(babelTypes, callArguments, descriptor) === void 0;
150
+ /** Name a callee or tag reads as, whether bare (`t`) or a method (`i18n.t`). */
151
+ const readCalleeName = (node) => {
152
+ if (babelTypes.isIdentifier(node)) return {
153
+ name: node.name,
154
+ isMethod: false
155
+ };
156
+ if (babelTypes.isMemberExpression(node) && babelTypes.isIdentifier(node.property) && !node.computed) return {
157
+ name: node.property.name,
158
+ isMethod: true
159
+ };
160
+ };
161
+ /** The id descriptor of `library` matching a callee name, if any. */
162
+ const findMessageIdCaller = (library, callee, shape) => {
163
+ if (!callee) return void 0;
164
+ return messageIdCallersByLibrary.get(library)?.find((descriptor) => descriptor.callerName === callee.name && (shape === "tagged-template" ? descriptor.matchAsTaggedTemplate === true : !callee.isMethod || descriptor.matchAsMethod === true));
165
+ };
166
+ /** Records the dictionary key read from one message call's arguments. */
167
+ const recordMessageCall = (library, descriptor, callArguments) => {
168
+ const resolved = require_compat_namespaceResolution.resolveNamespaceForAnalysis(babelTypes, callArguments, descriptor.namespaceSources);
169
+ recordRootScopeKey(library, typeof resolved === "string" && resolved !== "__default__" ? resolved : void 0);
170
+ };
171
+ /** Records one dictionary key for `library`, or poisons its binding. */
172
+ const recordRootScopeKey = (library, dictionaryKey) => {
173
+ const binding = rootScopeBindings.get(library);
174
+ if (binding === void 0) return;
175
+ if (dictionaryKey === void 0) {
176
+ rootScopeBindings.set(library, void 0);
177
+ return;
178
+ }
179
+ binding.add(dictionaryKey);
180
+ };
181
+ /**
182
+ * The dictionary an id-derived key binds: the key itself when the build
183
+ * produced such a dictionary, else the library's whole-file catalog
184
+ * (`messages` for lingui, `index` otherwise) with the id kept intact, else
185
+ * `undefined` — nothing to bind safely.
186
+ *
187
+ * Id-derived keys are guesses, so the set of dictionaries the build actually
188
+ * produced is required to tell a dictionary from a group inside the
189
+ * whole-file catalog.
190
+ */
191
+ const settleDictionaryKey = (key, descriptor) => {
192
+ if (!dictionaryModeMap) return void 0;
193
+ if (key in dictionaryModeMap) return key;
194
+ const rootKey = descriptor.rootDictionaryKey ?? "index";
195
+ return rootKey in dictionaryModeMap ? rootKey : void 0;
196
+ };
197
+ /**
198
+ * Settles every collected key of a library's root-scope binding, poisoning
199
+ * it when one key has nothing to bind — or when no id was read at all, since
200
+ * a call with nothing to bind must keep the registry.
201
+ */
202
+ const settleRootScopeBinding = (library, descriptor) => {
203
+ const binding = rootScopeBindings.get(library);
204
+ if (binding === void 0 || binding.size === 0) {
205
+ rootScopeBindings.set(library, void 0);
206
+ return;
207
+ }
208
+ const settled = /* @__PURE__ */ new Set();
209
+ for (const key of binding) {
210
+ const dictionaryKey = settleDictionaryKey(key, descriptor);
211
+ if (dictionaryKey === void 0) {
212
+ rootScopeBindings.set(library, void 0);
213
+ return;
214
+ }
215
+ settled.add(dictionaryKey);
216
+ }
217
+ rootScopeBindings.set(library, settled);
218
+ };
219
+ const readJsxSite = (node) => {
220
+ if (babelTypes.isJSXOpeningElement(node)) {
221
+ if (!babelTypes.isJSXIdentifier(node.name)) return void 0;
222
+ return {
223
+ localName: node.name.name,
224
+ readId: (attributeName) => require_staticAstReaders.readJsxAttributeString(babelTypes, node, attributeName),
225
+ addProp: (name, value) => {
226
+ node.attributes.push(babelTypes.jsxAttribute(babelTypes.jsxIdentifier(name), babelTypes.jsxExpressionContainer(value)));
227
+ }
228
+ };
229
+ }
230
+ const callee = readCalleeName(node.callee);
231
+ if (!callee || !COMPILED_JSX_FACTORY_PATTERN.test(callee.name)) return;
232
+ const [component, props] = node.arguments;
233
+ if (!babelTypes.isIdentifier(component) || !babelTypes.isObjectExpression(props)) return;
234
+ return {
235
+ localName: component.name,
236
+ readId: (attributeName) => {
237
+ const idNode = require_staticAstReaders.readObjectPropertyNode(babelTypes, props, attributeName);
238
+ if (idNode === "__default__") return void 0;
239
+ const id = require_staticAstReaders.readStaticString(babelTypes, idNode);
240
+ return id === void 0 ? void 0 : babelTypes.stringLiteral(id);
241
+ },
242
+ addProp: (name, value) => {
243
+ props.properties.push(babelTypes.objectProperty(babelTypes.identifier(name), value));
244
+ }
245
+ };
246
+ };
247
+ /** The compat JSX descriptor a site's component was imported as, if any. */
248
+ const jsxDescriptorFor = (site) => {
249
+ if (!site) return void 0;
250
+ const descriptor = callersByLocalName.get(site.localName);
251
+ return descriptor?.jsxIdAttribute !== void 0 ? descriptor : void 0;
252
+ };
253
+ /**
254
+ * The dictionary a compat JSX element binds, read from the first segment of
255
+ * its static id attribute — `undefined` when the id is dynamic or names
256
+ * nothing the build produced.
257
+ */
258
+ const resolveJsxDictionaryKey = (site, descriptor) => {
259
+ const idAttribute = site.readId(descriptor.jsxIdAttribute);
260
+ if (!idAttribute) return void 0;
261
+ const resolved = require_compat_namespaceResolution.resolveNamespaceForAnalysis(babelTypes, [idAttribute], descriptor.namespaceSources);
262
+ if (typeof resolved !== "string" || resolved === "__default__") return;
263
+ return settleDictionaryKey(resolved, descriptor);
264
+ };
70
265
  return {
71
266
  ownsImportSource: (importSource) => callerIndex.has(importSource),
72
267
  noteImport: (importSource, importedName, localName) => {
268
+ for (const [library, descriptors] of messageIdCallersByLibrary) if (descriptors.some((candidate) => candidate.callerName === importedName && candidate.importSources.includes(importSource))) addBareMessageCallerName(library, localName);
73
269
  const descriptor = callerIndex.get(importSource)?.get(importedName);
74
- if (descriptor) callersByLocalName.set(localName, descriptor);
270
+ if (!descriptor) return;
271
+ callersByLocalName.set(localName, descriptor);
272
+ if (descriptor.allowRootScope && messageIdCallersByLibrary.has(descriptor.library) && !rootScopeBindings.has(descriptor.library)) rootScopeBindings.set(descriptor.library, /* @__PURE__ */ new Set());
75
273
  },
76
274
  ownsLocalName: (localName) => callersByLocalName.has(localName),
275
+ noteDestructuredResult: (localCallerName, names) => {
276
+ const descriptor = callersByLocalName.get(localCallerName);
277
+ if (!descriptor?.allowRootScope) return;
278
+ const messageCallers = messageIdCallersByLibrary.get(descriptor.library);
279
+ if (!messageCallers) return;
280
+ for (const name of names) if (messageCallers.some((candidate) => candidate.callerName === name)) addBareMessageCallerName(descriptor.library, name);
281
+ },
77
282
  hasCallers: () => callersByLocalName.size > 0,
78
- analyzeCalls: (callNodes) => {
283
+ needsMessageIdSites: () => rootScopeBindings.size > 0,
284
+ ownsJsxSite: (node) => jsxDescriptorFor(readJsxSite(node)) !== void 0,
285
+ collectMessageIdSite: (node) => {
286
+ for (const library of rootScopeBindings.keys()) {
287
+ const jsxSite = babelTypes.isTaggedTemplateExpression(node) ? void 0 : readJsxSite(node);
288
+ if (jsxSite) {
289
+ const descriptor = messageIdCallersByLibrary.get(library)?.find((candidate) => candidate.callerName === jsxSite.localName && candidate.jsxIdAttribute !== void 0);
290
+ if (!descriptor) continue;
291
+ const idAttribute = jsxSite.readId(descriptor.jsxIdAttribute);
292
+ const resolved = idAttribute ? require_compat_namespaceResolution.resolveNamespaceForAnalysis(babelTypes, [idAttribute], descriptor.namespaceSources) : void 0;
293
+ recordRootScopeKey(library, typeof resolved === "string" && resolved !== "__default__" ? resolved : void 0);
294
+ continue;
295
+ }
296
+ if (babelTypes.isJSXOpeningElement(node)) continue;
297
+ if (babelTypes.isTaggedTemplateExpression(node)) {
298
+ if (findMessageIdCaller(library, readCalleeName(node.tag), "tagged-template")) recordRootScopeKey(library, void 0);
299
+ continue;
300
+ }
301
+ const callee = readCalleeName(node.callee);
302
+ const descriptor = findMessageIdCaller(library, callee, "call");
303
+ if (!descriptor || !callee) continue;
304
+ if (!callee.isMethod) {
305
+ pendingBareCalls.push({
306
+ library,
307
+ name: callee.name,
308
+ node
309
+ });
310
+ continue;
311
+ }
312
+ recordMessageCall(library, descriptor, node.arguments);
313
+ }
314
+ },
315
+ analyzeCalls: (callNodes, jsxSites = [], countReferences) => {
79
316
  if (callersByLocalName.size === 0) return;
80
317
  /**
81
318
  * Locals with at least one call site whose namespace could not be read:
@@ -85,11 +322,34 @@ const createCompatOptimizePass = (babelTypes, compatCallers, imports, buildModes
85
322
  */
86
323
  const unresolvableLocalNames = /* @__PURE__ */ new Set();
87
324
  let hasDynamicCall = false;
325
+ const settledLibraries = /* @__PURE__ */ new Set();
326
+ for (const { library, name, node } of pendingBareCalls) {
327
+ if (!bareMessageCallerNames.get(library)?.has(name)) continue;
328
+ const descriptor = findMessageIdCaller(library, {
329
+ name,
330
+ isMethod: false
331
+ }, "call");
332
+ if (descriptor) recordMessageCall(library, descriptor, node.arguments);
333
+ }
334
+ pendingBareCalls.length = 0;
88
335
  for (const callNode of callNodes) {
89
336
  const callee = callNode.callee;
90
337
  if (!babelTypes.isIdentifier(callee)) continue;
91
338
  const descriptor = callersByLocalName.get(callee.name);
92
339
  if (!descriptor) continue;
340
+ if (isRootScopeCall(descriptor, callNode.arguments)) {
341
+ if (!settledLibraries.has(descriptor.library)) {
342
+ settleRootScopeBinding(descriptor.library, descriptor);
343
+ settledLibraries.add(descriptor.library);
344
+ }
345
+ const binding = rootScopeBindings.get(descriptor.library);
346
+ if (binding === void 0) {
347
+ unresolvableLocalNames.add(callee.name);
348
+ continue;
349
+ }
350
+ for (const dictionaryKey of binding) if (isDynamicMode(dictionaryModeMap?.[dictionaryKey])) hasDynamicCall = true;
351
+ continue;
352
+ }
93
353
  const namespaceMatch = require_compat_namespaceResolution.resolveNamespaceForRewrite(babelTypes, callNode.arguments, descriptor);
94
354
  if (!namespaceMatch) {
95
355
  unresolvableLocalNames.add(callee.name);
@@ -98,6 +358,26 @@ const createCompatOptimizePass = (babelTypes, compatCallers, imports, buildModes
98
358
  const { dictionaryKey } = require_staticAstReaders.splitNamespace(namespaceMatch.fullNamespace);
99
359
  if (isDynamicMode(dictionaryModeMap?.[dictionaryKey])) hasDynamicCall = true;
100
360
  }
361
+ /** Bound elements seen per JSX caller local, to match against references. */
362
+ const jsxSiteCounts = /* @__PURE__ */ new Map();
363
+ for (const jsxNode of jsxSites) {
364
+ const site = readJsxSite(jsxNode);
365
+ const descriptor = jsxDescriptorFor(site);
366
+ if (!site || !descriptor) continue;
367
+ jsxSiteCounts.set(site.localName, (jsxSiteCounts.get(site.localName) ?? 0) + 1);
368
+ const dictionaryKey = resolveJsxDictionaryKey(site, descriptor);
369
+ if (dictionaryKey === void 0) {
370
+ unresolvableLocalNames.add(site.localName);
371
+ continue;
372
+ }
373
+ if (isDynamicMode(dictionaryModeMap?.[dictionaryKey])) hasDynamicCall = true;
374
+ }
375
+ for (const [localName, descriptor] of callersByLocalName) {
376
+ if (descriptor.jsxIdAttribute === void 0) continue;
377
+ const boundSites = jsxSiteCounts.get(localName) ?? 0;
378
+ const references = countReferences?.(localName) ?? boundSites;
379
+ if (boundSites === 0 || references !== boundSites) unresolvableLocalNames.add(localName);
380
+ }
101
381
  for (const localName of unresolvableLocalNames) callersByLocalName.delete(localName);
102
382
  useDynamicHelpers = isDynamicMode(importMode) || hasDynamicCall;
103
383
  },
@@ -107,6 +387,16 @@ const createCompatOptimizePass = (babelTypes, compatCallers, imports, buildModes
107
387
  const descriptor = callersByLocalName.get(callee.name);
108
388
  if (!descriptor) return;
109
389
  const callArguments = callNode.arguments;
390
+ if (isRootScopeCall(descriptor, callArguments)) {
391
+ const binding = rootScopeBindings.get(descriptor.library);
392
+ if (binding === void 0) return;
393
+ for (const dictionaryKey of binding) {
394
+ const mode = importModeFor(dictionaryKey);
395
+ const ident = babelTypes.identifier(imports.identFor(dictionaryKey, mode).name);
396
+ callArguments.push(mode === "static" ? ident : babelTypes.arrayExpression([ident, babelTypes.stringLiteral(dictionaryKey)]));
397
+ }
398
+ return;
399
+ }
110
400
  const namespaceMatch = require_compat_namespaceResolution.resolveNamespaceForRewrite(babelTypes, callArguments, descriptor);
111
401
  if (!namespaceMatch) return;
112
402
  const { dictionaryKey, keyPrefix } = require_staticAstReaders.splitNamespace(namespaceMatch.fullNamespace);
@@ -126,6 +416,16 @@ const createCompatOptimizePass = (babelTypes, compatCallers, imports, buildModes
126
416
  else namespaceMatch.optionsObject.properties = namespaceMatch.optionsObject.properties.filter((property) => property !== namespaceMatch.optionProperty);
127
417
  }
128
418
  },
419
+ rewriteJsxSite: (node) => {
420
+ const site = readJsxSite(node);
421
+ const descriptor = jsxDescriptorFor(site);
422
+ if (!site || !descriptor) return;
423
+ const dictionaryKey = resolveJsxDictionaryKey(site, descriptor);
424
+ if (dictionaryKey === void 0) return;
425
+ const mode = importModeFor(dictionaryKey);
426
+ const ident = babelTypes.identifier(imports.identFor(dictionaryKey, mode).name);
427
+ site.addProp(JSX_DICTIONARY_ATTRIBUTE, mode === "static" ? ident : babelTypes.arrayExpression([ident, babelTypes.stringLiteral(dictionaryKey)]));
428
+ },
129
429
  rewriteImportSpecifiers: (importNode) => {
130
430
  const byName = callerIndex.get(importNode.source.value);
131
431
  if (!byName) return;
@@ -1 +1 @@
1
- {"version":3,"file":"optimizePass.cjs","names":["getRewritableCallers","resolveNamespaceForRewrite","splitNamespace"],"sources":["../../../src/compat/optimizePass.ts"],"sourcesContent":["import type * as BabelTypes from '@babel/types';\nimport {\n type CallerDescriptor,\n getRewritableCallers,\n} from '@intlayer/config/callers';\nimport type {\n DictionaryImportRegistry,\n ImportMode,\n} from '../dictionaryImports';\nimport { splitNamespace } from '../staticAstReaders';\nimport { resolveNamespaceForRewrite } from './namespaceResolution';\n\n/**\n * Compat half of the optimize pass.\n *\n * Every adapter-specific behaviour of the build-time rewrite lives here:\n * matching a compat caller's import, resolving its namespace, and re-pointing\n * the import specifier at the dictionary-accepting helper.\n *\n * The pass works on plain AST nodes rather than `NodePath`s: the optimize\n * plugin collects the call sites in its single traversal and hands them over,\n * so nothing here walks the tree.\n *\n * It is created only when compat descriptors were injected by a compat\n * package's bundler plugin, so a plain intlayer build never allocates it —\n * {@link createCompatOptimizePass} returns `null` for an empty registry.\n */\nexport type CompatOptimizePass = {\n /**\n * Whether `importSource` exports at least one rewritable compat caller, so\n * the optimize plugin knows the import is worth inspecting even though it is\n * not a native intlayer package.\n */\n ownsImportSource: (importSource: string) => boolean;\n /** Records the local alias one import specifier introduces. */\n noteImport: (\n importSource: string,\n importedName: string,\n localName: string\n ) => void;\n /** Whether `localName` was bound to a compat caller by an import. */\n ownsLocalName: (localName: string) => boolean;\n /** Whether the file imported any compat caller at all. */\n hasCallers: () => boolean;\n /**\n * Resolves the namespace of every collected call, dropping the callers that\n * cannot be rewritten and deciding the file-level helper family. Must run\n * after every import was noted and before any rewrite.\n */\n analyzeCalls: (callNodes: readonly BabelTypes.CallExpression[]) => void;\n /** Rewrites one compat call site, if its caller survived {@link analyzeCalls}. */\n rewriteCall: (callNode: BabelTypes.CallExpression) => void;\n /** Re-points the compat specifiers of one import declaration. */\n rewriteImportSpecifiers: (importNode: BabelTypes.ImportDeclaration) => void;\n};\n\n/**\n * Rewritable slice of a descriptor registry, cached by array reference.\n *\n * Babel reuses one plugin-options object for every file of a build, so the\n * filter runs once per build instead of once per transformed file.\n */\nconst rewritableCallersCache = new WeakMap<\n readonly CallerDescriptor[],\n readonly CallerDescriptor[]\n>();\n\nconst getCachedRewritableCallers = (\n compatCallers: readonly CallerDescriptor[]\n): readonly CallerDescriptor[] => {\n let rewritable = rewritableCallersCache.get(compatCallers);\n\n if (!rewritable) {\n rewritable = getRewritableCallers([...compatCallers]);\n rewritableCallersCache.set(compatCallers, rewritable);\n }\n\n return rewritable;\n};\n\n/**\n * `importSource` → the callers it exports, indexed by their imported name.\n *\n * Built once per descriptor registry so matching an import specifier is two\n * map lookups instead of a scan over every descriptor's `importSources`.\n */\ntype CallerIndex = Map<string, Map<string, CallerDescriptor>>;\n\nconst callerIndexCache = new WeakMap<\n readonly CallerDescriptor[],\n CallerIndex\n>();\n\nconst getCallerIndex = (\n rewritableCallers: readonly CallerDescriptor[]\n): CallerIndex => {\n let index = callerIndexCache.get(rewritableCallers);\n\n if (!index) {\n index = new Map();\n\n for (const descriptor of rewritableCallers) {\n for (const importSource of descriptor.importSources) {\n let byName = index.get(importSource);\n\n if (!byName) {\n byName = new Map();\n index.set(importSource, byName);\n }\n\n byName.set(descriptor.callerName, descriptor);\n }\n }\n\n callerIndexCache.set(rewritableCallers, index);\n }\n\n return index;\n};\n\n/**\n * Builds the compat optimize pass for one source file, or `null` when no\n * compat caller was injected.\n *\n * @param babelTypes - Babel's type helpers.\n * @param compatCallers - Descriptors injected by the compat packages' plugins.\n * @param imports - Shared dictionary-import registry for the file.\n * @param buildModes - The file's global import mode and per-dictionary overrides.\n */\nexport const createCompatOptimizePass = (\n babelTypes: typeof BabelTypes,\n compatCallers: readonly CallerDescriptor[],\n imports: DictionaryImportRegistry,\n buildModes: {\n importMode: ImportMode | undefined;\n dictionaryModeMap?: Record<string, ImportMode | undefined>;\n }\n): CompatOptimizePass | null => {\n const rewritableCallers = getCachedRewritableCallers(compatCallers);\n if (rewritableCallers.length === 0) return null;\n\n const callerIndex = getCallerIndex(rewritableCallers);\n const { importMode, dictionaryModeMap } = buildModes;\n\n /** Local alias → the descriptor it was imported as. */\n const callersByLocalName = new Map<string, CallerDescriptor>();\n\n /**\n * File-level decision: one import specifier serves every call in the file, so\n * a global dynamic/fetch mode — or any per-dictionary override reached from\n * this file — flips all rewritten compat calls to the dynamic helper.\n */\n let useDynamicHelpers = false;\n\n /** Narrows to the two modes that resolve through a per-locale loader. */\n const isDynamicMode = (\n mode: ImportMode | undefined\n ): mode is 'dynamic' | 'fetch' => mode === 'dynamic' || mode === 'fetch';\n\n /** Import mode a compat call site resolves to for `dictionaryKey`. */\n const importModeFor = (dictionaryKey: string): ImportMode => {\n if (!useDynamicHelpers) return 'static';\n\n const override = dictionaryModeMap?.[dictionaryKey];\n if (isDynamicMode(override)) return override;\n\n return isDynamicMode(importMode) ? importMode : 'dynamic';\n };\n\n return {\n ownsImportSource: (importSource) => callerIndex.has(importSource),\n\n noteImport: (importSource, importedName, localName) => {\n const descriptor = callerIndex.get(importSource)?.get(importedName);\n if (descriptor) callersByLocalName.set(localName, descriptor);\n },\n\n ownsLocalName: (localName) => callersByLocalName.has(localName),\n\n hasCallers: () => callersByLocalName.size > 0,\n\n analyzeCalls: (callNodes) => {\n if (callersByLocalName.size === 0) return;\n\n /**\n * Locals with at least one call site whose namespace could not be read:\n * one import specifier serves every call in the file, so re-pointing it\n * while leaving those calls untouched would hand a raw namespace string\n * — or nothing at all — to the dictionary-accepting helper.\n */\n const unresolvableLocalNames = new Set<string>();\n let hasDynamicCall = false;\n\n for (const callNode of callNodes) {\n const callee = callNode.callee;\n if (!babelTypes.isIdentifier(callee)) continue;\n\n const descriptor = callersByLocalName.get(callee.name);\n if (!descriptor) continue;\n\n const namespaceMatch = resolveNamespaceForRewrite(\n babelTypes,\n callNode.arguments,\n descriptor\n );\n\n // No readable namespace — a computed key, or a call that passes none\n // at all (`useTranslations()`). Either way the dictionary is unknown at\n // build time, so the call keeps resolving through the runtime registry\n // and holds back the siblings sharing its import.\n if (!namespaceMatch) {\n unresolvableLocalNames.add(callee.name);\n continue;\n }\n\n const { dictionaryKey } = splitNamespace(namespaceMatch.fullNamespace);\n if (isDynamicMode(dictionaryModeMap?.[dictionaryKey])) {\n hasDynamicCall = true;\n }\n }\n\n for (const localName of unresolvableLocalNames) {\n callersByLocalName.delete(localName);\n }\n\n useDynamicHelpers = isDynamicMode(importMode) || hasDynamicCall;\n },\n\n rewriteCall: (callNode) => {\n const callee = callNode.callee;\n if (!babelTypes.isIdentifier(callee)) return;\n\n const descriptor = callersByLocalName.get(callee.name);\n if (!descriptor) return;\n\n const callArguments = callNode.arguments;\n const namespaceMatch = resolveNamespaceForRewrite(\n babelTypes,\n callArguments,\n descriptor\n );\n // Filtered out by `analyzeCalls` — the import keeps its original specifier.\n if (!namespaceMatch) return;\n\n const { dictionaryKey, keyPrefix } = splitNamespace(\n namespaceMatch.fullNamespace\n );\n const mode = importModeFor(dictionaryKey);\n const ident = imports.identFor(dictionaryKey, mode);\n const isDynamicHelper = mode !== 'static';\n\n if (namespaceMatch.argumentIndex !== undefined) {\n // Positional namespace: replace the string with the dictionary, then\n // the (dynamic) key and the (nested) prefix.\n // useTranslation('about.counter', opts)\n // static → useDictionary(_hash, 'counter', opts)\n // dynamic → useDictionaryDynamic(_hash_dyn, 'about', 'counter', opts)\n callArguments[namespaceMatch.argumentIndex] = babelTypes.identifier(\n ident.name\n );\n\n const insertedArguments: BabelTypes.Expression[] = [];\n if (isDynamicHelper) {\n insertedArguments.push(babelTypes.stringLiteral(dictionaryKey));\n }\n if (keyPrefix) {\n insertedArguments.push(babelTypes.stringLiteral(keyPrefix));\n }\n callArguments.splice(\n namespaceMatch.argumentIndex + 1,\n 0,\n ...insertedArguments\n );\n } else if (isDynamicHelper) {\n // Fixed / option namespace: prepend the loader and the dictionary key.\n callArguments.unshift(\n babelTypes.identifier(ident.name),\n babelTypes.stringLiteral(dictionaryKey)\n );\n } else {\n // Fixed / option namespace, static helper: prepend the dictionary.\n // useLingui() → useDictionary(_hash)\n callArguments.unshift(babelTypes.identifier(ident.name));\n }\n\n // Option namespace: leave only the key-prefix remainder in the options\n // object (or drop the property entirely), so the runtime helper does not\n // re-apply the dictionary key as a lookup prefix.\n // useI18n({ namespace: 'about' }) → useDictionary(_hash, {})\n if (namespaceMatch.optionProperty && namespaceMatch.optionsObject) {\n if (keyPrefix) {\n namespaceMatch.optionProperty.value =\n babelTypes.stringLiteral(keyPrefix);\n } else {\n namespaceMatch.optionsObject.properties =\n namespaceMatch.optionsObject.properties.filter(\n (property) => property !== namespaceMatch.optionProperty\n );\n }\n }\n },\n\n rewriteImportSpecifiers: (importNode) => {\n const byName = callerIndex.get(importNode.source.value);\n if (!byName) return;\n\n for (const specifier of importNode.specifiers) {\n if (!babelTypes.isImportSpecifier(specifier)) continue;\n\n // Only a specifier still held by `callersByLocalName` is rewritable —\n // `analyzeCalls` removed the ones with an unresolvable call site.\n const descriptor = callersByLocalName.get(specifier.local.name);\n if (!descriptor || !byName.has(descriptor.callerName)) continue;\n\n // Keep the local alias so call sites read unchanged; only the imported\n // name moves to the dictionary-accepting helper.\n specifier.imported = babelTypes.identifier(\n useDynamicHelpers\n ? descriptor.dynamicReplacement!\n : descriptor.staticReplacement!\n );\n }\n },\n };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"optimizePass.cjs","names":["getRewritableCallers","resolveNamespaceForRewrite","resolveNamespaceForAnalysis","readJsxAttributeString","readObjectPropertyNode","readStaticString","splitNamespace"],"sources":["../../../src/compat/optimizePass.ts"],"sourcesContent":["import type * as BabelTypes from '@babel/types';\nimport {\n type CallerDescriptor,\n getRewritableCallers,\n} from '@intlayer/config/callers';\nimport type {\n DictionaryImportRegistry,\n ImportMode,\n} from '../dictionaryImports';\nimport {\n ABSENT_VALUE,\n readJsxAttributeString,\n readObjectPropertyNode,\n readStaticString,\n splitNamespace,\n} from '../staticAstReaders';\nimport {\n resolveNamespaceForAnalysis,\n resolveNamespaceForRewrite,\n} from './namespaceResolution';\n\n/**\n * Compat half of the optimize pass.\n *\n * Every adapter-specific behaviour of the build-time rewrite lives here:\n * matching a compat caller's import, resolving its namespace, and re-pointing\n * the import specifier at the dictionary-accepting helper.\n *\n * The pass works on plain AST nodes rather than `NodePath`s: the optimize\n * plugin collects the call sites in its single traversal and hands them over,\n * so nothing here walks the tree.\n *\n * It is created only when compat descriptors were injected by a compat\n * package's bundler plugin, so a plain intlayer build never allocates it —\n * {@link createCompatOptimizePass} returns `null` for an empty registry.\n */\nexport type CompatOptimizePass = {\n /**\n * Whether `importSource` exports at least one rewritable compat caller, so\n * the optimize plugin knows the import is worth inspecting even though it is\n * not a native intlayer package.\n */\n ownsImportSource: (importSource: string) => boolean;\n /** Records the local alias one import specifier introduces. */\n noteImport: (\n importSource: string,\n importedName: string,\n localName: string\n ) => void;\n /** Whether `localName` was bound to a compat caller by an import. */\n ownsLocalName: (localName: string) => boolean;\n /**\n * Records the names a compat call's result was destructured into\n * (`const { i18n, _, t } = useLingui()`), so a bare `t('…')` call in the\n * file is read as a message-id site of that library. Names that are not\n * message callers of the library are ignored.\n */\n noteDestructuredResult: (localCallerName: string, names: string[]) => void;\n /** Whether the file imported any compat caller at all. */\n hasCallers: () => boolean;\n /**\n * Whether the file imported a root-scope caller whose dictionaries are\n * named by message ids (`useLingui()`), so the traversal must also hand\n * over the id-carrying sites through {@link collectMessageIdSite}. Valid\n * once every import was noted.\n */\n needsMessageIdSites: () => boolean;\n /**\n * Records one node that may carry a message id for a root-scope caller: a\n * call (`i18n._('footer.github')`, `t({ id })`), a tagged template\n * (``t`…` ``) or a JSX opening element (`<Trans id=\"…\" />`). Nodes that\n * match no message caller are ignored.\n */\n collectMessageIdSite: (\n node:\n | BabelTypes.CallExpression\n | BabelTypes.TaggedTemplateExpression\n | BabelTypes.JSXOpeningElement\n ) => void;\n /**\n * Whether the node is an element of a compat JSX caller bound by its id\n * attribute (`<Trans id=\"…\" />`) imported by the file — as source JSX, or\n * already compiled by the framework plugin into `jsx(Trans, { id })`.\n */\n ownsJsxSite: (node: JsxSiteNode) => boolean;\n /**\n * Resolves the namespace of every collected call and element, dropping the\n * callers that cannot be rewritten and deciding the file-level helper\n * family. Must run after every import was noted and every message-id site\n * collected, and before any rewrite.\n *\n * @param countReferences - Number of references a local name has in the\n * file. A JSX caller is only re-pointed when every reference is a bound\n * element: one passed around as a value (`component={Trans}`) would reach\n * the dictionary-accepting replacement without its dictionary.\n */\n analyzeCalls: (\n callNodes: readonly BabelTypes.CallExpression[],\n jsxSites?: readonly JsxSiteNode[],\n countReferences?: (localName: string) => number\n ) => void;\n /** Rewrites one compat call site, if its caller survived {@link analyzeCalls}. */\n rewriteCall: (callNode: BabelTypes.CallExpression) => void;\n /**\n * Binds one compat JSX element, if its caller survived {@link analyzeCalls}:\n * adds the `dictionary` prop naming the dictionary its id addresses.\n */\n rewriteJsxSite: (node: JsxSiteNode) => void;\n /** Re-points the compat specifiers of one import declaration. */\n rewriteImportSpecifiers: (importNode: BabelTypes.ImportDeclaration) => void;\n};\n\n/**\n * A compat JSX element as the pass may meet it: the source element, or the\n * call the automatic/classic JSX runtime compiled it into — the optimize\n * transform runs after the framework plugin, so on Vite the compiled form is\n * the usual one.\n */\nexport type JsxSiteNode =\n | BabelTypes.JSXOpeningElement\n | BabelTypes.CallExpression;\n\n/**\n * Factories a JSX element compiles into — `jsx(Trans, props)` /\n * `_jsxs(Trans, props)` / `jsxRuntime.jsxDEV(Trans, props)` for the automatic\n * runtime, `React.createElement(Trans, props)` for the classic one. The\n * transform aliases the import (`_jsx`), hence the optional underscores.\n */\nconst COMPILED_JSX_FACTORY_PATTERN = /^_*(jsxs?|jsxDEV|createElement)$/;\n\n/**\n * Rewritable slice of a descriptor registry, cached by array reference.\n *\n * Babel reuses one plugin-options object for every file of a build, so the\n * filter runs once per build instead of once per transformed file.\n */\nconst rewritableCallersCache = new WeakMap<\n readonly CallerDescriptor[],\n readonly CallerDescriptor[]\n>();\n\nconst getCachedRewritableCallers = (\n compatCallers: readonly CallerDescriptor[]\n): readonly CallerDescriptor[] => {\n let rewritable = rewritableCallersCache.get(compatCallers);\n\n if (!rewritable) {\n rewritable = getRewritableCallers([...compatCallers]);\n rewritableCallersCache.set(compatCallers, rewritable);\n }\n\n return rewritable;\n};\n\n/**\n * `importSource` → the callers it exports, indexed by their imported name.\n *\n * Built once per descriptor registry so matching an import specifier is two\n * map lookups instead of a scan over every descriptor's `importSources`.\n */\ntype CallerIndex = Map<string, Map<string, CallerDescriptor>>;\n\nconst callerIndexCache = new WeakMap<\n readonly CallerDescriptor[],\n CallerIndex\n>();\n\nconst getCallerIndex = (\n rewritableCallers: readonly CallerDescriptor[]\n): CallerIndex => {\n let index = callerIndexCache.get(rewritableCallers);\n\n if (!index) {\n index = new Map();\n\n for (const descriptor of rewritableCallers) {\n for (const importSource of descriptor.importSources) {\n let byName = index.get(importSource);\n\n if (!byName) {\n byName = new Map();\n index.set(importSource, byName);\n }\n\n byName.set(descriptor.callerName, descriptor);\n }\n }\n\n callerIndexCache.set(rewritableCallers, index);\n }\n\n return index;\n};\n\n/**\n * Prop through which a bound compat JSX element (`<Trans>`) receives its\n * dictionary — the static dictionary, or a `[loader, key]` pair in dynamic\n * mode. The compat package's replacement component reads it.\n */\nconst JSX_DICTIONARY_ATTRIBUTE = 'dictionary';\n\n/**\n * Whether a descriptor's call sites carry the message id themselves, with the\n * dictionary named by the id's first dot-segment (lingui's `_` / `t` /\n * `<Trans>`, react-intl's `formatMessage`). These are the sites a root-scope\n * binding reads its dictionaries from.\n */\nconst isMessageIdCaller = (descriptor: CallerDescriptor): boolean =>\n descriptor.translationFunction === 'self' &&\n descriptor.namespaceSources.some(\n (source) => source.from === 'path-first-segment'\n );\n\n/**\n * `library` → its message-id callers, cached by registry reference.\n *\n * Read from the full registry rather than its rewritable slice: the id\n * callers are matched as methods or JSX and are never rewritten themselves —\n * they only name the dictionaries a root-scope call in the same file binds.\n */\nconst messageIdCallersCache = new WeakMap<\n readonly CallerDescriptor[],\n Map<string, CallerDescriptor[]>\n>();\n\nconst getMessageIdCallersByLibrary = (\n compatCallers: readonly CallerDescriptor[]\n): Map<string, CallerDescriptor[]> => {\n let byLibrary = messageIdCallersCache.get(compatCallers);\n\n if (!byLibrary) {\n byLibrary = new Map();\n\n for (const descriptor of compatCallers) {\n if (!isMessageIdCaller(descriptor)) continue;\n\n let descriptors = byLibrary.get(descriptor.library);\n\n if (!descriptors) {\n descriptors = [];\n byLibrary.set(descriptor.library, descriptors);\n }\n\n descriptors.push(descriptor);\n }\n\n messageIdCallersCache.set(compatCallers, byLibrary);\n }\n\n return byLibrary;\n};\n\n/**\n * The dictionaries a library's root-scope calls bind in the current file:\n * the first segment of every static message id, or `undefined` once a single\n * id could not be read — a computed id may address any dictionary, so the\n * calls must keep resolving through the runtime registry.\n */\ntype RootScopeBinding = Set<string> | undefined;\n\n/**\n * Builds the compat optimize pass for one source file, or `null` when no\n * compat caller was injected.\n *\n * @param babelTypes - Babel's type helpers.\n * @param compatCallers - Descriptors injected by the compat packages' plugins.\n * @param imports - Shared dictionary-import registry for the file.\n * @param buildModes - The file's global import mode and per-dictionary overrides.\n */\nexport const createCompatOptimizePass = (\n babelTypes: typeof BabelTypes,\n compatCallers: readonly CallerDescriptor[],\n imports: DictionaryImportRegistry,\n buildModes: {\n importMode: ImportMode | undefined;\n dictionaryModeMap?: Record<string, ImportMode | undefined>;\n }\n): CompatOptimizePass | null => {\n const rewritableCallers = getCachedRewritableCallers(compatCallers);\n if (rewritableCallers.length === 0) return null;\n\n const callerIndex = getCallerIndex(rewritableCallers);\n const messageIdCallersByLibrary = getMessageIdCallersByLibrary(compatCallers);\n const { importMode, dictionaryModeMap } = buildModes;\n\n /** Local alias → the descriptor it was imported as. */\n const callersByLocalName = new Map<string, CallerDescriptor>();\n\n /**\n * Root-scope binding per library, filled from the message-id sites the\n * traversal hands over. A library is present only once a root-scope caller\n * of it was imported, so the sites of other libraries are never read.\n */\n const rootScopeBindings = new Map<string, RootScopeBinding>();\n\n /**\n * Local names a bare message call may use per library: message callers\n * imported from the library (`import { t } from '@lingui/macro'`) and\n * names destructured from a root-scope call's result\n * (`const { _ } = useLingui()`). A bare `t('…')` bound to anything else —\n * say a local wrapper — is not a message-id site, mirroring the usage\n * analyser; method calls (`i18n._('…')`) match by method name alone.\n */\n const bareMessageCallerNames = new Map<string, Set<string>>();\n\n /**\n * Bare calls met during the traversal, decided once every binding name is\n * known — a destructuring may sit below the call sites in source order.\n */\n const pendingBareCalls: {\n library: string;\n name: string;\n node: BabelTypes.CallExpression;\n }[] = [];\n\n const addBareMessageCallerName = (library: string, name: string): void => {\n let names = bareMessageCallerNames.get(library);\n if (!names) {\n names = new Set();\n bareMessageCallerNames.set(library, names);\n }\n names.add(name);\n };\n\n /**\n * File-level decision: one import specifier serves every call in the file, so\n * a global dynamic/fetch mode — or any per-dictionary override reached from\n * this file — flips all rewritten compat calls to the dynamic helper.\n */\n let useDynamicHelpers = false;\n\n /** Narrows to the two modes that resolve through a per-locale loader. */\n const isDynamicMode = (\n mode: ImportMode | undefined\n ): mode is 'dynamic' | 'fetch' => mode === 'dynamic' || mode === 'fetch';\n\n /** Import mode a compat call site resolves to for `dictionaryKey`. */\n const importModeFor = (dictionaryKey: string): ImportMode => {\n if (!useDynamicHelpers) return 'static';\n\n const override = dictionaryModeMap?.[dictionaryKey];\n if (isDynamicMode(override)) return override;\n\n return isDynamicMode(importMode) ? importMode : 'dynamic';\n };\n\n /**\n * Whether a call binds through the root-scope path: its descriptor allows\n * it, the library has id callers to read from, and the call itself names\n * no dictionary (`useLingui()`).\n */\n const isRootScopeCall = (\n descriptor: CallerDescriptor,\n callArguments: BabelTypes.CallExpression['arguments']\n ): boolean =>\n descriptor.allowRootScope === true &&\n messageIdCallersByLibrary.has(descriptor.library) &&\n resolveNamespaceForRewrite(babelTypes, callArguments, descriptor) ===\n undefined;\n\n /** Name a callee or tag reads as, whether bare (`t`) or a method (`i18n.t`). */\n const readCalleeName = (\n node: BabelTypes.Node\n ): { name: string; isMethod: boolean } | undefined => {\n if (babelTypes.isIdentifier(node)) {\n return { name: node.name, isMethod: false };\n }\n if (\n babelTypes.isMemberExpression(node) &&\n babelTypes.isIdentifier(node.property) &&\n !node.computed\n ) {\n return { name: node.property.name, isMethod: true };\n }\n return undefined;\n };\n\n /** The id descriptor of `library` matching a callee name, if any. */\n const findMessageIdCaller = (\n library: string,\n callee: { name: string; isMethod: boolean } | undefined,\n shape: 'call' | 'tagged-template'\n ): CallerDescriptor | undefined => {\n if (!callee) return undefined;\n\n return messageIdCallersByLibrary\n .get(library)\n ?.find(\n (descriptor) =>\n descriptor.callerName === callee.name &&\n (shape === 'tagged-template'\n ? descriptor.matchAsTaggedTemplate === true\n : !callee.isMethod || descriptor.matchAsMethod === true)\n );\n };\n\n /** Records the dictionary key read from one message call's arguments. */\n const recordMessageCall = (\n library: string,\n descriptor: CallerDescriptor,\n callArguments: BabelTypes.CallExpression['arguments']\n ): void => {\n const resolved = resolveNamespaceForAnalysis(\n babelTypes,\n callArguments,\n descriptor.namespaceSources\n );\n\n recordRootScopeKey(\n library,\n typeof resolved === 'string' && resolved !== ABSENT_VALUE\n ? resolved\n : undefined\n );\n };\n\n /** Records one dictionary key for `library`, or poisons its binding. */\n const recordRootScopeKey = (\n library: string,\n dictionaryKey: string | undefined\n ): void => {\n const binding = rootScopeBindings.get(library);\n // Already poisoned: nothing can un-poison a file.\n if (binding === undefined) return;\n\n if (dictionaryKey === undefined) {\n rootScopeBindings.set(library, undefined);\n return;\n }\n\n binding.add(dictionaryKey);\n };\n\n /**\n * The dictionary an id-derived key binds: the key itself when the build\n * produced such a dictionary, else the library's whole-file catalog\n * (`messages` for lingui, `index` otherwise) with the id kept intact, else\n * `undefined` — nothing to bind safely.\n *\n * Id-derived keys are guesses, so the set of dictionaries the build actually\n * produced is required to tell a dictionary from a group inside the\n * whole-file catalog.\n */\n const settleDictionaryKey = (\n key: string,\n descriptor: CallerDescriptor\n ): string | undefined => {\n if (!dictionaryModeMap) return undefined;\n if (key in dictionaryModeMap) return key;\n\n const rootKey = descriptor.rootDictionaryKey ?? 'index';\n return rootKey in dictionaryModeMap ? rootKey : undefined;\n };\n\n /**\n * Settles every collected key of a library's root-scope binding, poisoning\n * it when one key has nothing to bind — or when no id was read at all, since\n * a call with nothing to bind must keep the registry.\n */\n const settleRootScopeBinding = (\n library: string,\n descriptor: CallerDescriptor\n ): void => {\n const binding = rootScopeBindings.get(library);\n if (binding === undefined || binding.size === 0) {\n rootScopeBindings.set(library, undefined);\n return;\n }\n\n const settled = new Set<string>();\n\n for (const key of binding) {\n const dictionaryKey = settleDictionaryKey(key, descriptor);\n if (dictionaryKey === undefined) {\n rootScopeBindings.set(library, undefined);\n return;\n }\n settled.add(dictionaryKey);\n }\n\n rootScopeBindings.set(library, settled);\n };\n\n /**\n * A JSX element in either of its shapes, reduced to what the pass needs:\n * the component's local name, its static id, and a way to add a prop.\n */\n type JsxSite = {\n localName: string;\n /** The static id attribute as a string node, `undefined` when dynamic or absent. */\n readId: (attributeName: string) => BabelTypes.StringLiteral | undefined;\n addProp: (name: string, value: BabelTypes.Expression) => void;\n };\n\n const readJsxSite = (node: JsxSiteNode): JsxSite | undefined => {\n if (babelTypes.isJSXOpeningElement(node)) {\n if (!babelTypes.isJSXIdentifier(node.name)) return undefined;\n\n return {\n localName: node.name.name,\n readId: (attributeName) =>\n readJsxAttributeString(babelTypes, node, attributeName),\n addProp: (name, value) => {\n node.attributes.push(\n babelTypes.jsxAttribute(\n babelTypes.jsxIdentifier(name),\n babelTypes.jsxExpressionContainer(value)\n )\n );\n },\n };\n }\n\n // Compiled form: `jsx(Trans, { id: \"…\" })` / `React.createElement(Trans, {…})`.\n const callee = readCalleeName(node.callee);\n if (!callee || !COMPILED_JSX_FACTORY_PATTERN.test(callee.name)) {\n return undefined;\n }\n\n const [component, props] = node.arguments;\n if (\n !babelTypes.isIdentifier(component) ||\n !babelTypes.isObjectExpression(props)\n ) {\n return undefined;\n }\n\n return {\n localName: component.name,\n readId: (attributeName) => {\n const idNode = readObjectPropertyNode(babelTypes, props, attributeName);\n if (idNode === ABSENT_VALUE) return undefined;\n const id = readStaticString(babelTypes, idNode);\n return id === undefined ? undefined : babelTypes.stringLiteral(id);\n },\n addProp: (name, value) => {\n props.properties.push(\n babelTypes.objectProperty(babelTypes.identifier(name), value)\n );\n },\n };\n };\n\n /** The compat JSX descriptor a site's component was imported as, if any. */\n const jsxDescriptorFor = (\n site: JsxSite | undefined\n ): CallerDescriptor | undefined => {\n if (!site) return undefined;\n const descriptor = callersByLocalName.get(site.localName);\n return descriptor?.jsxIdAttribute !== undefined ? descriptor : undefined;\n };\n\n /**\n * The dictionary a compat JSX element binds, read from the first segment of\n * its static id attribute — `undefined` when the id is dynamic or names\n * nothing the build produced.\n */\n const resolveJsxDictionaryKey = (\n site: JsxSite,\n descriptor: CallerDescriptor\n ): string | undefined => {\n const idAttribute = site.readId(descriptor.jsxIdAttribute!);\n if (!idAttribute) return undefined;\n\n const resolved = resolveNamespaceForAnalysis(\n babelTypes,\n [idAttribute],\n descriptor.namespaceSources\n );\n if (typeof resolved !== 'string' || resolved === ABSENT_VALUE) {\n return undefined;\n }\n\n return settleDictionaryKey(resolved, descriptor);\n };\n\n return {\n ownsImportSource: (importSource) => callerIndex.has(importSource),\n\n noteImport: (importSource, importedName, localName) => {\n for (const [library, descriptors] of messageIdCallersByLibrary) {\n if (\n descriptors.some(\n (candidate) =>\n candidate.callerName === importedName &&\n candidate.importSources.includes(importSource)\n )\n ) {\n addBareMessageCallerName(library, localName);\n }\n }\n\n const descriptor = callerIndex.get(importSource)?.get(importedName);\n if (!descriptor) return;\n\n callersByLocalName.set(localName, descriptor);\n\n if (\n descriptor.allowRootScope &&\n messageIdCallersByLibrary.has(descriptor.library) &&\n !rootScopeBindings.has(descriptor.library)\n ) {\n rootScopeBindings.set(descriptor.library, new Set());\n }\n },\n\n ownsLocalName: (localName) => callersByLocalName.has(localName),\n\n noteDestructuredResult: (localCallerName, names) => {\n const descriptor = callersByLocalName.get(localCallerName);\n if (!descriptor?.allowRootScope) return;\n\n const messageCallers = messageIdCallersByLibrary.get(descriptor.library);\n if (!messageCallers) return;\n\n for (const name of names) {\n if (messageCallers.some((candidate) => candidate.callerName === name)) {\n addBareMessageCallerName(descriptor.library, name);\n }\n }\n },\n\n hasCallers: () => callersByLocalName.size > 0,\n\n needsMessageIdSites: () => rootScopeBindings.size > 0,\n\n ownsJsxSite: (node) => jsxDescriptorFor(readJsxSite(node)) !== undefined,\n\n collectMessageIdSite: (node) => {\n for (const library of rootScopeBindings.keys()) {\n // An element — as JSX or as the call it compiled into — matched by\n // the component's name against the library's JSX id callers.\n const jsxSite = babelTypes.isTaggedTemplateExpression(node)\n ? undefined\n : readJsxSite(node);\n if (jsxSite) {\n const descriptor = messageIdCallersByLibrary\n .get(library)\n ?.find(\n (candidate) =>\n candidate.callerName === jsxSite.localName &&\n candidate.jsxIdAttribute !== undefined\n );\n if (!descriptor) continue;\n\n const idAttribute = jsxSite.readId(descriptor.jsxIdAttribute!);\n const resolved = idAttribute\n ? resolveNamespaceForAnalysis(\n babelTypes,\n [idAttribute],\n descriptor.namespaceSources\n )\n : undefined;\n\n recordRootScopeKey(\n library,\n typeof resolved === 'string' && resolved !== ABSENT_VALUE\n ? resolved\n : undefined\n );\n continue;\n }\n\n if (babelTypes.isJSXOpeningElement(node)) continue;\n\n if (babelTypes.isTaggedTemplateExpression(node)) {\n // A macro template (``t`Hello ${name}` ``) has no id until the\n // lingui macro plugin compiles it, so reaching one here means the\n // id is unknowable.\n const descriptor = findMessageIdCaller(\n library,\n readCalleeName(node.tag),\n 'tagged-template'\n );\n if (descriptor) recordRootScopeKey(library, undefined);\n continue;\n }\n\n const callee = readCalleeName(node.callee);\n const descriptor = findMessageIdCaller(library, callee, 'call');\n if (!descriptor || !callee) continue;\n\n if (!callee.isMethod) {\n pendingBareCalls.push({ library, name: callee.name, node });\n continue;\n }\n\n recordMessageCall(library, descriptor, node.arguments);\n }\n },\n\n analyzeCalls: (callNodes, jsxSites = [], countReferences) => {\n if (callersByLocalName.size === 0) return;\n\n /**\n * Locals with at least one call site whose namespace could not be read:\n * one import specifier serves every call in the file, so re-pointing it\n * while leaving those calls untouched would hand a raw namespace string\n * — or nothing at all — to the dictionary-accepting helper.\n */\n const unresolvableLocalNames = new Set<string>();\n let hasDynamicCall = false;\n const settledLibraries = new Set<string>();\n\n for (const { library, name, node } of pendingBareCalls) {\n if (!bareMessageCallerNames.get(library)?.has(name)) continue;\n\n const descriptor = findMessageIdCaller(\n library,\n { name, isMethod: false },\n 'call'\n );\n if (descriptor) recordMessageCall(library, descriptor, node.arguments);\n }\n pendingBareCalls.length = 0;\n\n for (const callNode of callNodes) {\n const callee = callNode.callee;\n if (!babelTypes.isIdentifier(callee)) continue;\n\n const descriptor = callersByLocalName.get(callee.name);\n if (!descriptor) continue;\n\n if (isRootScopeCall(descriptor, callNode.arguments)) {\n if (!settledLibraries.has(descriptor.library)) {\n settleRootScopeBinding(descriptor.library, descriptor);\n settledLibraries.add(descriptor.library);\n }\n\n const binding = rootScopeBindings.get(descriptor.library);\n if (binding === undefined) {\n unresolvableLocalNames.add(callee.name);\n continue;\n }\n\n for (const dictionaryKey of binding) {\n if (isDynamicMode(dictionaryModeMap?.[dictionaryKey])) {\n hasDynamicCall = true;\n }\n }\n continue;\n }\n\n const namespaceMatch = resolveNamespaceForRewrite(\n babelTypes,\n callNode.arguments,\n descriptor\n );\n\n // No readable namespace — a computed key, or a call that passes none\n // at all (`useTranslations()`). Either way the dictionary is unknown at\n // build time, so the call keeps resolving through the runtime registry\n // and holds back the siblings sharing its import.\n if (!namespaceMatch) {\n unresolvableLocalNames.add(callee.name);\n continue;\n }\n\n const { dictionaryKey } = splitNamespace(namespaceMatch.fullNamespace);\n if (isDynamicMode(dictionaryModeMap?.[dictionaryKey])) {\n hasDynamicCall = true;\n }\n }\n\n /** Bound elements seen per JSX caller local, to match against references. */\n const jsxSiteCounts = new Map<string, number>();\n\n for (const jsxNode of jsxSites) {\n const site = readJsxSite(jsxNode);\n const descriptor = jsxDescriptorFor(site);\n if (!site || !descriptor) continue;\n\n jsxSiteCounts.set(\n site.localName,\n (jsxSiteCounts.get(site.localName) ?? 0) + 1\n );\n\n const dictionaryKey = resolveJsxDictionaryKey(site, descriptor);\n if (dictionaryKey === undefined) {\n unresolvableLocalNames.add(site.localName);\n continue;\n }\n\n if (isDynamicMode(dictionaryModeMap?.[dictionaryKey])) {\n hasDynamicCall = true;\n }\n }\n\n // A JSX caller is re-pointed only when every reference to it is an\n // element the pass binds — the import specifier serves them all.\n for (const [localName, descriptor] of callersByLocalName) {\n if (descriptor.jsxIdAttribute === undefined) continue;\n\n const boundSites = jsxSiteCounts.get(localName) ?? 0;\n const references = countReferences?.(localName) ?? boundSites;\n if (boundSites === 0 || references !== boundSites) {\n unresolvableLocalNames.add(localName);\n }\n }\n\n for (const localName of unresolvableLocalNames) {\n callersByLocalName.delete(localName);\n }\n\n useDynamicHelpers = isDynamicMode(importMode) || hasDynamicCall;\n },\n\n rewriteCall: (callNode) => {\n const callee = callNode.callee;\n if (!babelTypes.isIdentifier(callee)) return;\n\n const descriptor = callersByLocalName.get(callee.name);\n if (!descriptor) return;\n\n const callArguments = callNode.arguments;\n\n if (isRootScopeCall(descriptor, callArguments)) {\n // Filtered out by `analyzeCalls` when poisoned — never reached here.\n const binding = rootScopeBindings.get(descriptor.library);\n if (binding === undefined) return;\n\n // Root scope: every dictionary named in the file, ids kept intact so\n // the runtime picks the dictionary by first segment.\n // useLingui()\n // static → useDictionary(_footer, _header)\n // dynamic → useDictionaryDynamic([_footer_dyn, 'footer'], …)\n for (const dictionaryKey of binding) {\n const mode = importModeFor(dictionaryKey);\n const ident = babelTypes.identifier(\n imports.identFor(dictionaryKey, mode).name\n );\n\n callArguments.push(\n mode === 'static'\n ? ident\n : babelTypes.arrayExpression([\n ident,\n babelTypes.stringLiteral(dictionaryKey),\n ])\n );\n }\n return;\n }\n\n const namespaceMatch = resolveNamespaceForRewrite(\n babelTypes,\n callArguments,\n descriptor\n );\n // Filtered out by `analyzeCalls` — the import keeps its original specifier.\n if (!namespaceMatch) return;\n\n const { dictionaryKey, keyPrefix } = splitNamespace(\n namespaceMatch.fullNamespace\n );\n const mode = importModeFor(dictionaryKey);\n const ident = imports.identFor(dictionaryKey, mode);\n const isDynamicHelper = mode !== 'static';\n\n if (namespaceMatch.argumentIndex !== undefined) {\n // Positional namespace: replace the string with the dictionary, then\n // the (dynamic) key and the (nested) prefix.\n // useTranslation('about.counter', opts)\n // static → useDictionary(_hash, 'counter', opts)\n // dynamic → useDictionaryDynamic(_hash_dyn, 'about', 'counter', opts)\n callArguments[namespaceMatch.argumentIndex] = babelTypes.identifier(\n ident.name\n );\n\n const insertedArguments: BabelTypes.Expression[] = [];\n if (isDynamicHelper) {\n insertedArguments.push(babelTypes.stringLiteral(dictionaryKey));\n }\n if (keyPrefix) {\n insertedArguments.push(babelTypes.stringLiteral(keyPrefix));\n }\n callArguments.splice(\n namespaceMatch.argumentIndex + 1,\n 0,\n ...insertedArguments\n );\n } else if (isDynamicHelper) {\n // Fixed / option namespace: prepend the loader and the dictionary key.\n callArguments.unshift(\n babelTypes.identifier(ident.name),\n babelTypes.stringLiteral(dictionaryKey)\n );\n } else {\n // Fixed / option namespace, static helper: prepend the dictionary.\n callArguments.unshift(babelTypes.identifier(ident.name));\n }\n\n // Option namespace: leave only the key-prefix remainder in the options\n // object (or drop the property entirely), so the runtime helper does not\n // re-apply the dictionary key as a lookup prefix.\n // useI18n({ namespace: 'about' }) → useDictionary(_hash, {})\n if (namespaceMatch.optionProperty && namespaceMatch.optionsObject) {\n if (keyPrefix) {\n namespaceMatch.optionProperty.value =\n babelTypes.stringLiteral(keyPrefix);\n } else {\n namespaceMatch.optionsObject.properties =\n namespaceMatch.optionsObject.properties.filter(\n (property) => property !== namespaceMatch.optionProperty\n );\n }\n }\n },\n\n rewriteJsxSite: (node) => {\n const site = readJsxSite(node);\n const descriptor = jsxDescriptorFor(site);\n if (!site || !descriptor) return;\n\n // Filtered out by `analyzeCalls` when unresolvable — never reached here.\n const dictionaryKey = resolveJsxDictionaryKey(site, descriptor);\n if (dictionaryKey === undefined) return;\n\n const mode = importModeFor(dictionaryKey);\n const ident = babelTypes.identifier(\n imports.identFor(dictionaryKey, mode).name\n );\n\n // The id stays intact; the element receives the dictionary it names.\n // <Trans id=\"footer.github\" />\n // static → <Trans id=\"footer.github\" dictionary={_footer} />\n // dynamic → <Trans id=\"footer.github\" dictionary={[_footer_dyn, 'footer']} />\n site.addProp(\n JSX_DICTIONARY_ATTRIBUTE,\n mode === 'static'\n ? ident\n : babelTypes.arrayExpression([\n ident,\n babelTypes.stringLiteral(dictionaryKey),\n ])\n );\n },\n\n rewriteImportSpecifiers: (importNode) => {\n const byName = callerIndex.get(importNode.source.value);\n if (!byName) return;\n\n for (const specifier of importNode.specifiers) {\n if (!babelTypes.isImportSpecifier(specifier)) continue;\n\n // Only a specifier still held by `callersByLocalName` is rewritable —\n // `analyzeCalls` removed the ones with an unresolvable call site.\n const descriptor = callersByLocalName.get(specifier.local.name);\n if (!descriptor || !byName.has(descriptor.callerName)) continue;\n\n // Keep the local alias so call sites read unchanged; only the imported\n // name moves to the dictionary-accepting helper.\n specifier.imported = babelTypes.identifier(\n useDynamicHelpers\n ? descriptor.dynamicReplacement!\n : descriptor.staticReplacement!\n );\n }\n },\n };\n};\n"],"mappings":"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"}
@@ -32,11 +32,7 @@ const getOptimizePluginOptions = (params) => {
32
32
  const unmergedDictionariesEntryPath = (0, node_path.join)(mainDir, "unmerged_dictionaries.mjs");
33
33
  const dynamicDictionariesEntryPath = (0, node_path.join)(mainDir, "dynamic_dictionaries.mjs");
34
34
  const fetchDictionariesEntryPath = (0, node_path.join)(mainDir, "fetch_dictionaries.mjs");
35
- const filesList = [
36
- ...(0, _intlayer_engine_utils.buildComponentFilesList)(config),
37
- dictionariesEntryPath,
38
- unmergedDictionariesEntryPath
39
- ];
35
+ const filesList = [...(0, _intlayer_engine_utils.buildComponentFilesList)(config), dictionariesEntryPath];
40
36
  const dictionaries = providedDictionaries ?? loadDictionaries(config);
41
37
  const dictionaryModeMap = {};
42
38
  dictionaries.forEach((dictionary) => {
@@ -1 +1 @@
1
- {"version":3,"file":"getOptimizePluginOptions.cjs","names":["getConfiguration","join","buildComponentFilesList","getNestedDictionaryGraph"],"sources":["../../src/getOptimizePluginOptions.ts"],"sourcesContent":["import { join } from 'node:path';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport { getNestedDictionaryGraph } from '@intlayer/core/dictionaryManipulator';\nimport { buildComponentFilesList } from '@intlayer/engine/utils';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport type { OptimizePluginOptions } from './babel-plugin-intlayer-optimize';\n\ntype GetOptimizePluginOptionsParams = {\n /**\n * Configuration options for loading intlayer config\n */\n configOptions?: GetConfigurationOptions;\n /**\n * Pre-loaded dictionaries (optional - will be loaded if not provided)\n */\n dictionaries?: Dictionary[];\n /**\n * Whether the current transform is for an SSR bundle. When using Babel\n * directly (e.g. in `babel.config.js`), forward the bundler's server flag\n * here so SSR-specific static dictionary transforms apply.\n */\n isServer?: boolean;\n /**\n * Override specific options\n */\n overrides?: Partial<OptimizePluginOptions>;\n};\n\n/**\n * Load dictionaries from the dictionaries-entry package\n */\nconst loadDictionaries = (config: IntlayerConfig): Dictionary[] => {\n try {\n // Dynamic require to avoid build-time dependency issues\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { getDictionaries } = require('@intlayer/dictionaries-entry');\n const dictionariesRecord = getDictionaries(config);\n\n return Object.values(dictionariesRecord);\n } catch {\n // If dictionaries-entry is not available, return empty array\n return [];\n }\n};\n\n/**\n * Get the options for the Intlayer Babel optimization plugin\n * This function loads the Intlayer configuration and returns the paths\n * needed for dictionary optimization and import rewriting.\n */\nexport const getOptimizePluginOptions = (\n params?: GetOptimizePluginOptionsParams\n): OptimizePluginOptions => {\n const {\n configOptions,\n dictionaries: providedDictionaries,\n isServer,\n overrides,\n } = params ?? {};\n\n const config = getConfiguration(configOptions);\n const {\n mainDir,\n dictionariesDir,\n unmergedDictionariesDir,\n dynamicDictionariesDir,\n fetchDictionariesDir,\n } = config.system;\n const importMode = config.build.importMode ?? config.dictionary?.importMode;\n const optimize = config.build.optimize;\n\n const dictionariesEntryPath = join(mainDir, 'dictionaries.mjs');\n const unmergedDictionariesEntryPath = join(\n mainDir,\n 'unmerged_dictionaries.mjs'\n );\n const dynamicDictionariesEntryPath = join(\n mainDir,\n 'dynamic_dictionaries.mjs'\n );\n const fetchDictionariesEntryPath = join(mainDir, 'fetch_dictionaries.mjs');\n\n const filesListPattern = buildComponentFilesList(config);\n\n const filesList = [\n ...filesListPattern,\n dictionariesEntryPath, // should add dictionariesEntryPath to replace it by an empty object if import made dynamic\n unmergedDictionariesEntryPath, // should add dictionariesEntryPath to replace it by an empty object if import made dynamic\n ];\n\n // Load dictionaries if not provided\n const dictionaries = providedDictionaries ?? loadDictionaries(config);\n\n const dictionaryModeMap: Record<\n string,\n 'static' | 'dynamic' | 'fetch' | undefined\n > = {};\n\n dictionaries.forEach((dictionary) => {\n dictionaryModeMap[dictionary.key] = dictionary.importMode ?? importMode;\n });\n\n // Dictionaries holding `nest()` references: their static import is re-pointed\n // at the companion module carrying the nest targets.\n const nestingDictionaryKeys = [\n ...getNestedDictionaryGraph(dictionaries).keys(),\n ];\n\n return {\n optimize,\n dictionariesDir,\n dictionariesEntryPath,\n unmergedDictionariesDir,\n unmergedDictionariesEntryPath,\n dynamicDictionariesDir,\n dynamicDictionariesEntryPath,\n fetchDictionariesDir,\n fetchDictionariesEntryPath,\n replaceDictionaryEntry: true,\n nestingDictionaryKeys,\n importMode,\n dictionaryModeMap,\n filesList,\n isServer,\n ...overrides,\n };\n};\n"],"mappings":";;;;;;;;;;AAmCA,MAAM,oBAAoB,WAAyC;CACjE,IAAI;EAGF,MAAM,EAAE,oBAAoB,QAAQ,8BAA8B;EAClE,MAAM,qBAAqB,gBAAgB,MAAM;EAEjD,OAAO,OAAO,OAAO,kBAAkB;CACzC,QAAQ;EAEN,OAAO,CAAC;CACV;AACF;;;;;;AAOA,MAAa,4BACX,WAC0B;CAC1B,MAAM,EACJ,eACA,cAAc,sBACd,UACA,cACE,UAAU,CAAC;CAEf,MAAM,aAASA,wCAAiB,aAAa;CAC7C,MAAM,EACJ,SACA,iBACA,yBACA,wBACA,yBACE,OAAO;CACX,MAAM,aAAa,OAAO,MAAM,cAAc,OAAO,YAAY;CACjE,MAAM,WAAW,OAAO,MAAM;CAE9B,MAAM,4BAAwBC,gBAAK,SAAS,kBAAkB;CAC9D,MAAM,oCAAgCA,gBACpC,SACA,2BACF;CACA,MAAM,mCAA+BA,gBACnC,SACA,0BACF;CACA,MAAM,iCAA6BA,gBAAK,SAAS,wBAAwB;CAIzE,MAAM,YAAY;EAChB,OAHuBC,gDAAwB,MAG7B;EAClB;EACA;CACF;CAGA,MAAM,eAAe,wBAAwB,iBAAiB,MAAM;CAEpE,MAAM,oBAGF,CAAC;CAEL,aAAa,SAAS,eAAe;EACnC,kBAAkB,WAAW,OAAO,WAAW,cAAc;CAC/D,CAAC;CAQD,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,wBAAwB;EACxB,wBAdA,OAAGC,+DAAyB,YAAY,CAAC,CAAC,KAAK,CAc3B;EACpB;EACA;EACA;EACA;EACA,GAAG;CACL;AACF"}
1
+ {"version":3,"file":"getOptimizePluginOptions.cjs","names":["getConfiguration","join","buildComponentFilesList","getNestedDictionaryGraph"],"sources":["../../src/getOptimizePluginOptions.ts"],"sourcesContent":["import { join } from 'node:path';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport { getNestedDictionaryGraph } from '@intlayer/core/dictionaryManipulator';\nimport { buildComponentFilesList } from '@intlayer/engine/utils';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport type { OptimizePluginOptions } from './babel-plugin-intlayer-optimize';\n\ntype GetOptimizePluginOptionsParams = {\n /**\n * Configuration options for loading intlayer config\n */\n configOptions?: GetConfigurationOptions;\n /**\n * Pre-loaded dictionaries (optional - will be loaded if not provided)\n */\n dictionaries?: Dictionary[];\n /**\n * Whether the current transform is for an SSR bundle. When using Babel\n * directly (e.g. in `babel.config.js`), forward the bundler's server flag\n * here so SSR-specific static dictionary transforms apply.\n */\n isServer?: boolean;\n /**\n * Override specific options\n */\n overrides?: Partial<OptimizePluginOptions>;\n};\n\n/**\n * Load dictionaries from the dictionaries-entry package\n */\nconst loadDictionaries = (config: IntlayerConfig): Dictionary[] => {\n try {\n // Dynamic require to avoid build-time dependency issues\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { getDictionaries } = require('@intlayer/dictionaries-entry');\n const dictionariesRecord = getDictionaries(config);\n\n return Object.values(dictionariesRecord);\n } catch {\n // If dictionaries-entry is not available, return empty array\n return [];\n }\n};\n\n/**\n * Get the options for the Intlayer Babel optimization plugin\n * This function loads the Intlayer configuration and returns the paths\n * needed for dictionary optimization and import rewriting.\n */\nexport const getOptimizePluginOptions = (\n params?: GetOptimizePluginOptionsParams\n): OptimizePluginOptions => {\n const {\n configOptions,\n dictionaries: providedDictionaries,\n isServer,\n overrides,\n } = params ?? {};\n\n const config = getConfiguration(configOptions);\n const {\n mainDir,\n dictionariesDir,\n unmergedDictionariesDir,\n dynamicDictionariesDir,\n fetchDictionariesDir,\n } = config.system;\n const importMode = config.build.importMode ?? config.dictionary?.importMode;\n const optimize = config.build.optimize;\n\n const dictionariesEntryPath = join(mainDir, 'dictionaries.mjs');\n const unmergedDictionariesEntryPath = join(\n mainDir,\n 'unmerged_dictionaries.mjs'\n );\n const dynamicDictionariesEntryPath = join(\n mainDir,\n 'dynamic_dictionaries.mjs'\n );\n const fetchDictionariesEntryPath = join(mainDir, 'fetch_dictionaries.mjs');\n\n const filesListPattern = buildComponentFilesList(config);\n\n const filesList = [\n ...filesListPattern,\n dictionariesEntryPath, // should add dictionariesEntryPath to replace it by an empty object if import made dynamic\n ];\n\n // Load dictionaries if not provided\n const dictionaries = providedDictionaries ?? loadDictionaries(config);\n\n const dictionaryModeMap: Record<\n string,\n 'static' | 'dynamic' | 'fetch' | undefined\n > = {};\n\n dictionaries.forEach((dictionary) => {\n dictionaryModeMap[dictionary.key] = dictionary.importMode ?? importMode;\n });\n\n // Dictionaries holding `nest()` references: their static import is re-pointed\n // at the companion module carrying the nest targets.\n const nestingDictionaryKeys = [\n ...getNestedDictionaryGraph(dictionaries).keys(),\n ];\n\n return {\n optimize,\n dictionariesDir,\n dictionariesEntryPath,\n unmergedDictionariesDir,\n unmergedDictionariesEntryPath,\n dynamicDictionariesDir,\n dynamicDictionariesEntryPath,\n fetchDictionariesDir,\n fetchDictionariesEntryPath,\n replaceDictionaryEntry: true,\n nestingDictionaryKeys,\n importMode,\n dictionaryModeMap,\n filesList,\n isServer,\n ...overrides,\n };\n};\n"],"mappings":";;;;;;;;;;AAmCA,MAAM,oBAAoB,WAAyC;CACjE,IAAI;EAGF,MAAM,EAAE,oBAAoB,QAAQ,8BAA8B;EAClE,MAAM,qBAAqB,gBAAgB,MAAM;EAEjD,OAAO,OAAO,OAAO,kBAAkB;CACzC,QAAQ;EAEN,OAAO,CAAC;CACV;AACF;;;;;;AAOA,MAAa,4BACX,WAC0B;CAC1B,MAAM,EACJ,eACA,cAAc,sBACd,UACA,cACE,UAAU,CAAC;CAEf,MAAM,aAASA,wCAAiB,aAAa;CAC7C,MAAM,EACJ,SACA,iBACA,yBACA,wBACA,yBACE,OAAO;CACX,MAAM,aAAa,OAAO,MAAM,cAAc,OAAO,YAAY;CACjE,MAAM,WAAW,OAAO,MAAM;CAE9B,MAAM,4BAAwBC,gBAAK,SAAS,kBAAkB;CAC9D,MAAM,oCAAgCA,gBACpC,SACA,2BACF;CACA,MAAM,mCAA+BA,gBACnC,SACA,0BACF;CACA,MAAM,iCAA6BA,gBAAK,SAAS,wBAAwB;CAIzE,MAAM,YAAY,CAChB,OAHuBC,gDAAwB,MAG7B,GAClB,qBACF;CAGA,MAAM,eAAe,wBAAwB,iBAAiB,MAAM;CAEpE,MAAM,oBAGF,CAAC;CAEL,aAAa,SAAS,eAAe;EACnC,kBAAkB,WAAW,OAAO,WAAW,cAAc;CAC/D,CAAC;CAQD,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,wBAAwB;EACxB,wBAdA,OAAGC,+DAAyB,YAAY,CAAC,CAAC,KAAK,CAc3B;EACpB;EACA;EACA;EACA;EACA,GAAG;CACL;AACF"}
@@ -264,11 +264,9 @@ const intlayerOptimizeBabelPlugin = (babel) => {
264
264
  enter(programPath, state) {
265
265
  const filename = state.file.opts.filename ? normalizePath(state.file.opts.filename) : void 0;
266
266
  const dictionariesEntryPath = state.opts.dictionariesEntryPath ? normalizePath(state.opts.dictionariesEntryPath) : void 0;
267
- const unmergedDictionariesEntryPath = state.opts.unmergedDictionariesEntryPath ? normalizePath(state.opts.unmergedDictionariesEntryPath) : void 0;
268
- const entryAccessorName = filename ? filename === dictionariesEntryPath ? "getDictionaries" : filename === unmergedDictionariesEntryPath ? "getUnmergedDictionaries" : void 0 : void 0;
269
- if (state.opts.replaceDictionaryEntry && entryAccessorName) {
267
+ if (state.opts.replaceDictionaryEntry && filename && filename === dictionariesEntryPath) {
270
268
  state._isDictEntry = true;
271
- programPath.node.body = [t.exportDefaultDeclaration(t.objectExpression([])), t.exportNamedDeclaration(t.variableDeclaration("const", [t.variableDeclarator(t.identifier(entryAccessorName), t.arrowFunctionExpression([], t.objectExpression([])))]))];
269
+ programPath.node.body = [t.exportDefaultDeclaration(t.objectExpression([])), t.exportNamedDeclaration(t.variableDeclaration("const", [t.variableDeclarator(t.identifier("getDictionaries"), t.arrowFunctionExpression([], t.objectExpression([])))]))];
272
270
  }
273
271
  },
274
272
  /**
@@ -317,12 +315,34 @@ const intlayerOptimizeBabelPlugin = (babel) => {
317
315
  }
318
316
  const nativeCallNodes = [];
319
317
  const compatCallNodes = [];
320
- if (callerMap.size > 0 || compat?.hasCallers()) programPath.traverse({ CallExpression(path) {
321
- const callee = path.node.callee;
322
- if (!t.isIdentifier(callee)) return;
323
- if (callerMap.has(callee.name)) nativeCallNodes.push(path.node);
324
- else if (compat?.ownsLocalName(callee.name)) compatCallNodes.push(path.node);
325
- } });
318
+ /**
319
+ * Compat JSX elements bound by their id attribute (`<Trans id>`),
320
+ * as source JSX or as the `jsx(Trans, …)` call the framework plugin
321
+ * already compiled them into.
322
+ */
323
+ const compatJsxNodes = [];
324
+ const collectMessageIds = compat?.needsMessageIdSites() ?? false;
325
+ if (callerMap.size > 0 || compat?.hasCallers()) programPath.traverse({
326
+ CallExpression(path) {
327
+ if (collectMessageIds) compat?.collectMessageIdSite(path.node);
328
+ if (compat?.ownsJsxSite(path.node)) compatJsxNodes.push(path.node);
329
+ const callee = path.node.callee;
330
+ if (!t.isIdentifier(callee)) return;
331
+ if (callerMap.has(callee.name)) nativeCallNodes.push(path.node);
332
+ else if (compat?.ownsLocalName(callee.name)) {
333
+ compatCallNodes.push(path.node);
334
+ const declarator = path.parent;
335
+ if (t.isVariableDeclarator(declarator) && t.isObjectPattern(declarator.id)) compat.noteDestructuredResult(callee.name, declarator.id.properties.flatMap((property) => t.isObjectProperty(property) && t.isIdentifier(property.value) ? [property.value.name] : []));
336
+ }
337
+ },
338
+ ...collectMessageIds ? { TaggedTemplateExpression(path) {
339
+ compat?.collectMessageIdSite(path.node);
340
+ } } : {},
341
+ ...compat ? { JSXOpeningElement(path) {
342
+ if (collectMessageIds) compat.collectMessageIdSite(path.node);
343
+ if (compat.ownsJsxSite(path.node)) compatJsxNodes.push(path.node);
344
+ } } : {}
345
+ });
326
346
  const packagesWithDynamicCall = /* @__PURE__ */ new Set();
327
347
  const packagesWithFetchCall = /* @__PURE__ */ new Set();
328
348
  for (const callNode of nativeCallNodes) {
@@ -336,7 +356,7 @@ const intlayerOptimizeBabelPlugin = (babel) => {
336
356
  if (overrideMode === "dynamic") packagesWithDynamicCall.add(callerPackage);
337
357
  else if (overrideMode === "fetch") packagesWithFetchCall.add(callerPackage);
338
358
  }
339
- compat?.analyzeCalls(compatCallNodes);
359
+ compat?.analyzeCalls(compatCallNodes, compatJsxNodes, (localName) => programPath.scope.getBinding(localName)?.referencePaths.length ?? 0);
340
360
  const helperPlanCache = /* @__PURE__ */ new Map();
341
361
  const getHelperPlan = (packageName) => {
342
362
  let plan = helperPlanCache.get(packageName);
@@ -374,6 +394,7 @@ const intlayerOptimizeBabelPlugin = (babel) => {
374
394
  }
375
395
  }
376
396
  for (const callNode of compatCallNodes) compat?.rewriteCall(callNode);
397
+ for (const jsxNode of compatJsxNodes) compat?.rewriteJsxSite(jsxNode);
377
398
  for (const callNode of nativeCallNodes) {
378
399
  const callee = callNode.callee;
379
400
  const originalImportedName = callerMap.get(callee.name);