@intlayer/babel 9.4.3 → 9.5.0

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.
Files changed (133) hide show
  1. package/README.md +0 -2
  2. package/dist/cjs/analyzeContentUsage.cjs +301 -0
  3. package/dist/cjs/analyzeContentUsage.cjs.map +1 -0
  4. package/dist/cjs/babel-plugin-intlayer-field-rename.cjs +5 -4
  5. package/dist/cjs/babel-plugin-intlayer-field-rename.cjs.map +1 -1
  6. package/dist/cjs/babel-plugin-intlayer-optimize.cjs +173 -282
  7. package/dist/cjs/babel-plugin-intlayer-optimize.cjs.map +1 -1
  8. package/dist/cjs/babel-plugin-intlayer-purge.cjs +4 -3
  9. package/dist/cjs/babel-plugin-intlayer-purge.cjs.map +1 -1
  10. package/dist/cjs/babel-plugin-intlayer-usage-analyzer.cjs +49 -699
  11. package/dist/cjs/babel-plugin-intlayer-usage-analyzer.cjs.map +1 -1
  12. package/dist/cjs/compat/namespaceResolution.cjs +124 -0
  13. package/dist/cjs/compat/namespaceResolution.cjs.map +1 -0
  14. package/dist/cjs/compat/optimizePass.cjs +144 -0
  15. package/dist/cjs/compat/optimizePass.cjs.map +1 -0
  16. package/dist/cjs/compat/usageAnalysis.cjs +195 -0
  17. package/dist/cjs/compat/usageAnalysis.cjs.map +1 -0
  18. package/dist/cjs/dictionaryImports.cjs +40 -0
  19. package/dist/cjs/dictionaryImports.cjs.map +1 -0
  20. package/dist/cjs/extractContent/utils/extractDictionaryInfo.cjs +1 -1
  21. package/dist/cjs/getExtractPluginOptions.cjs +1 -1
  22. package/dist/cjs/getOptimizePluginOptions.cjs +1 -1
  23. package/dist/cjs/getPurgePluginOptions.cjs +1 -1
  24. package/dist/cjs/index.cjs +9 -5
  25. package/dist/cjs/nativeCallers.cjs +41 -0
  26. package/dist/cjs/nativeCallers.cjs.map +1 -0
  27. package/dist/cjs/pruneContext.cjs +79 -0
  28. package/dist/cjs/pruneContext.cjs.map +1 -0
  29. package/dist/cjs/staticAstReaders.cjs +140 -0
  30. package/dist/cjs/staticAstReaders.cjs.map +1 -0
  31. package/dist/cjs/transformers.cjs +2 -1
  32. package/dist/cjs/transformers.cjs.map +1 -1
  33. package/dist/esm/analyzeContentUsage.mjs +300 -0
  34. package/dist/esm/analyzeContentUsage.mjs.map +1 -0
  35. package/dist/esm/babel-plugin-intlayer-field-rename.mjs +3 -2
  36. package/dist/esm/babel-plugin-intlayer-field-rename.mjs.map +1 -1
  37. package/dist/esm/babel-plugin-intlayer-optimize.mjs +173 -282
  38. package/dist/esm/babel-plugin-intlayer-optimize.mjs.map +1 -1
  39. package/dist/esm/babel-plugin-intlayer-purge.mjs +3 -2
  40. package/dist/esm/babel-plugin-intlayer-purge.mjs.map +1 -1
  41. package/dist/esm/babel-plugin-intlayer-usage-analyzer.mjs +43 -694
  42. package/dist/esm/babel-plugin-intlayer-usage-analyzer.mjs.map +1 -1
  43. package/dist/esm/compat/namespaceResolution.mjs +120 -0
  44. package/dist/esm/compat/namespaceResolution.mjs.map +1 -0
  45. package/dist/esm/compat/optimizePass.mjs +143 -0
  46. package/dist/esm/compat/optimizePass.mjs.map +1 -0
  47. package/dist/esm/compat/usageAnalysis.mjs +193 -0
  48. package/dist/esm/compat/usageAnalysis.mjs.map +1 -0
  49. package/dist/esm/dictionaryImports.mjs +38 -0
  50. package/dist/esm/dictionaryImports.mjs.map +1 -0
  51. package/dist/esm/extractContent/utils/extractDictionaryInfo.mjs +1 -1
  52. package/dist/esm/getExtractPluginOptions.mjs +1 -1
  53. package/dist/esm/getOptimizePluginOptions.mjs +1 -1
  54. package/dist/esm/getPurgePluginOptions.mjs +1 -1
  55. package/dist/esm/index.mjs +5 -2
  56. package/dist/esm/nativeCallers.mjs +38 -0
  57. package/dist/esm/nativeCallers.mjs.map +1 -0
  58. package/dist/esm/pruneContext.mjs +76 -0
  59. package/dist/esm/pruneContext.mjs.map +1 -0
  60. package/dist/esm/staticAstReaders.mjs +129 -0
  61. package/dist/esm/staticAstReaders.mjs.map +1 -0
  62. package/dist/esm/transformers.mjs +2 -1
  63. package/dist/esm/transformers.mjs.map +1 -1
  64. package/dist/types/analyzeContentUsage.d.ts +18 -0
  65. package/dist/types/analyzeContentUsage.d.ts.map +1 -0
  66. package/dist/types/babel-plugin-intlayer-extract.d.ts +3 -4
  67. package/dist/types/babel-plugin-intlayer-extract.d.ts.map +1 -1
  68. package/dist/types/babel-plugin-intlayer-field-rename.d.ts +10 -10
  69. package/dist/types/babel-plugin-intlayer-field-rename.d.ts.map +1 -1
  70. package/dist/types/babel-plugin-intlayer-minify.d.ts +2 -3
  71. package/dist/types/babel-plugin-intlayer-minify.d.ts.map +1 -1
  72. package/dist/types/babel-plugin-intlayer-optimize.d.ts +11 -18
  73. package/dist/types/babel-plugin-intlayer-optimize.d.ts.map +1 -1
  74. package/dist/types/babel-plugin-intlayer-purge.d.ts +7 -7
  75. package/dist/types/babel-plugin-intlayer-purge.d.ts.map +1 -1
  76. package/dist/types/babel-plugin-intlayer-usage-analyzer.d.ts +22 -189
  77. package/dist/types/babel-plugin-intlayer-usage-analyzer.d.ts.map +1 -1
  78. package/dist/types/babelParser.d.ts +3 -4
  79. package/dist/types/babelParser.d.ts.map +1 -1
  80. package/dist/types/compat/namespaceResolution.d.ts +66 -0
  81. package/dist/types/compat/namespaceResolution.d.ts.map +1 -0
  82. package/dist/types/compat/optimizePass.d.ts +58 -0
  83. package/dist/types/compat/optimizePass.d.ts.map +1 -0
  84. package/dist/types/compat/usageAnalysis.d.ts +75 -0
  85. package/dist/types/compat/usageAnalysis.d.ts.map +1 -0
  86. package/dist/types/dictionaryImports.d.ts +46 -0
  87. package/dist/types/dictionaryImports.d.ts.map +1 -0
  88. package/dist/types/extractContent/babelProcessor.d.ts +4 -5
  89. package/dist/types/extractContent/babelProcessor.d.ts.map +1 -1
  90. package/dist/types/extractContent/contentWriter.d.ts +3 -4
  91. package/dist/types/extractContent/contentWriter.d.ts.map +1 -1
  92. package/dist/types/extractContent/extractContent.d.ts +3 -4
  93. package/dist/types/extractContent/extractContent.d.ts.map +1 -1
  94. package/dist/types/extractContent/processTsxFile.d.ts +2 -3
  95. package/dist/types/extractContent/processTsxFile.d.ts.map +1 -1
  96. package/dist/types/extractContent/utils/constants.d.ts +3 -4
  97. package/dist/types/extractContent/utils/constants.d.ts.map +1 -1
  98. package/dist/types/extractContent/utils/detectPackageName.d.ts +1 -2
  99. package/dist/types/extractContent/utils/detectPackageName.d.ts.map +1 -1
  100. package/dist/types/extractContent/utils/extractDictionaryInfo.d.ts +5 -6
  101. package/dist/types/extractContent/utils/extractDictionaryInfo.d.ts.map +1 -1
  102. package/dist/types/extractContent/utils/extractDictionaryKey.d.ts +2 -3
  103. package/dist/types/extractContent/utils/extractDictionaryKey.d.ts.map +1 -1
  104. package/dist/types/extractContent/utils/generateKey.d.ts +1 -2
  105. package/dist/types/extractContent/utils/generateKey.d.ts.map +1 -1
  106. package/dist/types/extractContent/utils/getComponentName.d.ts +1 -2
  107. package/dist/types/extractContent/utils/getComponentName.d.ts.map +1 -1
  108. package/dist/types/extractContent/utils/getExistingIntlayerInfo.d.ts +2 -3
  109. package/dist/types/extractContent/utils/getExistingIntlayerInfo.d.ts.map +1 -1
  110. package/dist/types/extractContent/utils/getOrGenerateKey.d.ts +1 -2
  111. package/dist/types/extractContent/utils/getOrGenerateKey.d.ts.map +1 -1
  112. package/dist/types/extractContent/utils/resolveDictionaryKey.d.ts +1 -2
  113. package/dist/types/extractContent/utils/resolveDictionaryKey.d.ts.map +1 -1
  114. package/dist/types/extractScriptBlocks.d.ts +3 -4
  115. package/dist/types/extractScriptBlocks.d.ts.map +1 -1
  116. package/dist/types/getExtractPluginOptions.d.ts +2 -3
  117. package/dist/types/getExtractPluginOptions.d.ts.map +1 -1
  118. package/dist/types/getOptimizePluginOptions.d.ts +1 -2
  119. package/dist/types/getOptimizePluginOptions.d.ts.map +1 -1
  120. package/dist/types/getPurgePluginOptions.d.ts +2 -3
  121. package/dist/types/getPurgePluginOptions.d.ts.map +1 -1
  122. package/dist/types/index.d.ts +5 -2
  123. package/dist/types/nativeCallers.d.ts +33 -0
  124. package/dist/types/nativeCallers.d.ts.map +1 -0
  125. package/dist/types/normalizedFilesList.d.ts +1 -2
  126. package/dist/types/normalizedFilesList.d.ts.map +1 -1
  127. package/dist/types/pruneContext.d.ts +152 -0
  128. package/dist/types/pruneContext.d.ts.map +1 -0
  129. package/dist/types/staticAstReaders.d.ts +84 -0
  130. package/dist/types/staticAstReaders.d.ts.map +1 -0
  131. package/dist/types/transformers.d.ts +11 -11
  132. package/dist/types/transformers.d.ts.map +1 -1
  133. package/package.json +12 -12
@@ -1,695 +1,63 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_nativeCallers = require('./nativeCallers.cjs');
3
+ const require_pruneContext = require('./pruneContext.cjs');
4
+ const require_staticAstReaders = require('./staticAstReaders.cjs');
5
+ const require_analyzeContentUsage = require('./analyzeContentUsage.cjs');
6
+ const require_compat_usageAnalysis = require('./compat/usageAnalysis.cjs');
7
+
2
8
  //#region src/babel-plugin-intlayer-usage-analyzer.ts
3
- const createPruneContext = () => ({
4
- dictionaryKeyToFieldUsageMap: /* @__PURE__ */ new Map(),
5
- dictionariesWithEdgeCases: /* @__PURE__ */ new Set(),
6
- hasUnparsableSourceFiles: false,
7
- dictionaryKeysWithUntrackedBindings: /* @__PURE__ */ new Map(),
8
- dictionaryKeyToFieldRenameMap: /* @__PURE__ */ new Map(),
9
- dictionaryKeysWithOpaqueFields: /* @__PURE__ */ new Map(),
10
- dictionariesSkippingFieldRename: /* @__PURE__ */ new Set(),
11
- pendingFrameworkAnalysis: /* @__PURE__ */ new Map()
12
- });
13
9
  /**
14
- * Records `nest()` references in a prune context so nest targets stay
15
- * resolvable — and no larger than they need to be — after purge and minify.
16
- *
17
- * A nested node stores the ORIGINAL dot path of the field it reads
18
- * (`nest('common', 'period')` → `'period'`) and `getNesting` resolves it at
19
- * runtime against the compiled JSON. That drives three adjustments:
20
- *
21
- * - Nest targets never take part in field renaming, since the stored path
22
- * would no longer match the renamed keys.
23
- * - A target already consumed by a call site keeps its analysed fields plus
24
- * the nested ones, so neither consumer loses content.
25
- * - A target reached *only* through `nest()` is given a usage entry holding
26
- * exactly the nested fields. Without one, the purge pass leaves the whole
27
- * dictionary untouched; with one, it trims the dictionary down to what the
28
- * nest references actually read. This matters most for `dynamic` / `fetch`
29
- * dictionaries, whose full static snapshot would otherwise be pulled into
30
- * the eager bundle just to satisfy a single nested field.
31
- *
32
- * Seeding is skipped when a source file failed to parse: that file could
33
- * reference the dictionary in a way the analysis never saw, which is the same
34
- * guard the purge pass applies to keys with no usage entry.
35
- *
36
- * @param pruneContext - Shared analysis state to update in place.
37
- * @param nestedDictionaryReferences - Nest-target key → referenced top-level
38
- * fields, as returned by `getNestedDictionaryReferences`.
39
- */
40
- const preserveNestedDictionaryFields = (pruneContext, nestedDictionaryReferences) => {
41
- for (const [dictionaryKey, nestedFields] of nestedDictionaryReferences.entries()) {
42
- pruneContext.dictionariesSkippingFieldRename.add(dictionaryKey);
43
- const currentFieldUsage = pruneContext.dictionaryKeyToFieldUsageMap.get(dictionaryKey);
44
- if (nestedFields === "all") {
45
- pruneContext.dictionaryKeyToFieldUsageMap.set(dictionaryKey, "all");
46
- continue;
47
- }
48
- if (!currentFieldUsage) {
49
- if (pruneContext.hasUnparsableSourceFiles) continue;
50
- pruneContext.dictionaryKeyToFieldUsageMap.set(dictionaryKey, new Set(nestedFields));
51
- continue;
52
- }
53
- if (currentFieldUsage === "all") continue;
54
- for (const nestedField of nestedFields) currentFieldUsage.add(nestedField);
55
- }
56
- };
57
- /**
58
- * Methods the runtime attaches to a resolved content object rather than fields
59
- * declared in a `.content` file.
60
- *
61
- * `vanilla-intlayer`'s `useIntlayer` / `useDictionary` assign `onChange` onto
62
- * the content they return, so `useIntlayer('app').onChange(cb)` is a
63
- * subscription — not a read of a field named `onChange`. Without this list the
64
- * analyser records `onChange` as the only consumed field and the purge pass
65
- * strips every real field from the compiled dictionary.
66
- *
67
- * The analyser looks *through* these calls: the callback parameter is analysed
68
- * as a fresh content root, and the call result is analysed again because the
69
- * helpers return the content object for chaining.
70
- */
71
- const CHAINABLE_RUNTIME_METHOD_NAMES = /* @__PURE__ */ new Set(["onChange"]);
72
- /** Canonical intlayer caller names that trigger usage analysis. */
73
- const INTLAYER_CALLER_NAMES = [
74
- "useIntlayer",
75
- "getIntlayer",
76
- "getIntlayerAsync"
77
- ];
78
- /**
79
- * Default registry of compat namespace callers.
80
- *
81
- * Intentionally empty — compat-specific caller configurations belong in their
82
- * respective adapter packages (e.g. `@intlayer/react-i18next/plugin`,
83
- * `@intlayer/vue-i18n/plugin`) and are injected into `makeUsageAnalyzerBabelPlugin`
84
- * via the `compatCallers` option. Centralising them here would couple the
85
- * core `@intlayer/babel` package to every compat adapter, which violates the
86
- * design principle that compat logic lives in compat packages.
10
+ * @deprecated Always empty. Compat callers are injected per build through
11
+ * `options.compatCallers`; there is no default registry to fall back to.
87
12
  */
88
13
  const DEFAULT_COMPAT_CALLERS = [];
89
- /** Default namespace used by compat callers when no namespace argument is given. */
90
- const DEFAULT_COMPAT_NAMESPACE = "translation";
91
- /**
92
- * Climbs past an enclosing `await` expression so that
93
- * `const t = await getTranslations('ns')` — or `await getIntlayerAsync('key')`
94
- * — is resolved to its variable declarator the same way the synchronous form
95
- * is.
96
- */
97
- const unwrapAwait = (babelTypes, path) => {
98
- const parentPath = path.parentPath;
99
- if (parentPath && babelTypes.isAwaitExpression(parentPath.node)) return parentPath;
100
- return path;
101
- };
102
- /**
103
- * Records the usage of a specific dictionary key's fields into `pruneContext`.
104
- * Merges with any previously recorded usage for the same key.
105
- */
106
- const recordFieldUsage = (pruneContext, dictionaryKey, fieldUsage) => {
107
- const existingUsage = pruneContext.dictionaryKeyToFieldUsageMap.get(dictionaryKey);
108
- if (existingUsage === "all") return;
109
- if (fieldUsage === "all") {
110
- pruneContext.dictionaryKeyToFieldUsageMap.set(dictionaryKey, "all");
111
- return;
112
- }
113
- if (existingUsage instanceof Set) {
114
- for (const fieldName of fieldUsage) existingUsage.add(fieldName);
115
- return;
116
- }
117
- pruneContext.dictionaryKeyToFieldUsageMap.set(dictionaryKey, new Set(fieldUsage));
118
- };
119
- /**
120
- * Analyses how the result of a single `useIntlayer('key')` / `getIntlayer('key')`
121
- * call expression is consumed, then records the field usage into `pruneContext`.
122
- *
123
- * Recognised patterns:
124
- * const { fieldA, fieldB } = useIntlayer('key') → records {fieldA, fieldB}
125
- * useIntlayer('key').fieldA → records {fieldA}
126
- * useIntlayer('key')['fieldA'] → records {fieldA}
127
- * const { ...rest } = useIntlayer('key') → records 'all' (spread)
128
- * const result = useIntlayer('key') → records 'all' (untracked binding)
129
- */
130
- const analyzeCallExpressionUsage = (babelTypes, pruneContext, callExpressionPath, dictionaryKey, currentSourceFilePath, isSfcFile) => {
131
- /** Mark the dictionary key as having an untracked binding in this file. */
132
- const markUntrackedBinding = () => {
133
- const existingPaths = pruneContext.dictionaryKeysWithUntrackedBindings.get(dictionaryKey) ?? [];
134
- if (!existingPaths.includes(currentSourceFilePath)) pruneContext.dictionaryKeysWithUntrackedBindings.set(dictionaryKey, [...existingPaths, currentSourceFilePath]);
135
- recordFieldUsage(pruneContext, dictionaryKey, "all");
136
- };
137
- /** Record that the content value at `fieldPath` is consumed opaquely. */
138
- const markOpaqueField = (fieldPath, line) => {
139
- const pathToOccurrence = pruneContext.dictionaryKeysWithOpaqueFields.get(dictionaryKey) ?? /* @__PURE__ */ new Map();
140
- const location = line !== void 0 ? `${currentSourceFilePath}:${line}` : currentSourceFilePath;
141
- const joinedFieldPath = fieldPath.join(".");
142
- const occurrence = pathToOccurrence.get(joinedFieldPath) ?? {
143
- fieldPath: [...fieldPath],
144
- locations: []
145
- };
146
- if (!occurrence.locations.includes(location)) occurrence.locations.push(location);
147
- pathToOccurrence.set(joinedFieldPath, occurrence);
148
- pruneContext.dictionaryKeysWithOpaqueFields.set(dictionaryKey, pathToOccurrence);
149
- };
150
- /** Register a plain variable binding in an SFC file for a second-pass analysis. */
151
- const deferFrameworkAnalysis = (variableName) => {
152
- const existing = pruneContext.pendingFrameworkAnalysis.get(currentSourceFilePath) ?? [];
153
- if (!existing.some((e) => e.variableName === variableName && e.dictionaryKey === dictionaryKey)) existing.push({
154
- variableName,
155
- dictionaryKey
156
- });
157
- pruneContext.pendingFrameworkAnalysis.set(currentSourceFilePath, existing);
158
- };
159
- /**
160
- * Analyses how the content value at `fieldPath` (currently referenced by
161
- * `refPath`) is consumed, to detect opaque consumption (passing a dictionary
162
- * value as-is to a prop or function argument).
163
- *
164
- * Member-access chains and destructuring are followed recursively with an
165
- * extended field path, so the *terminal* consumption of a chain like
166
- * `content.sections.hero` decides opacity — mirroring how far the
167
- * source-code renamer can rewrite accesses. When the terminal consumption
168
- * escapes static tracking, the deepest reached field path is marked opaque
169
- * so the children of that value keep their original key names.
170
- */
171
- const analyzeOpaqueUsage = (refPath, fieldPath) => {
172
- const parentNode = refPath.parent;
173
- const parentPath = refPath.parentPath;
174
- if (parentPath && (babelTypes.isMemberExpression(parentNode) || babelTypes.isOptionalMemberExpression(parentNode)) && parentNode.object === refPath.node) {
175
- const memberNode = parentNode;
176
- if (memberNode.computed && babelTypes.isNumericLiteral(memberNode.property)) {
177
- analyzeOpaqueUsage(parentPath, fieldPath);
178
- return;
179
- }
180
- if (!memberNode.computed && babelTypes.isIdentifier(memberNode.property)) {
181
- analyzeOpaqueUsage(parentPath, [...fieldPath, memberNode.property.name]);
182
- return;
183
- }
184
- if (memberNode.computed && babelTypes.isStringLiteral(memberNode.property)) {
185
- analyzeOpaqueUsage(parentPath, [...fieldPath, memberNode.property.value]);
186
- return;
187
- }
188
- markOpaqueField(fieldPath, refPath.node.loc?.start.line);
189
- return;
190
- }
191
- if (babelTypes.isVariableDeclarator(parentNode) && babelTypes.isObjectPattern(parentNode.id) && parentNode.init === refPath.node) {
192
- analyzeOpaqueDestructuring(parentNode.id, refPath, fieldPath);
193
- return;
194
- }
195
- if (babelTypes.isArrayExpression(parentNode)) return;
196
- markOpaqueField(fieldPath, refPath.node.loc?.start.line);
197
- };
198
- /**
199
- * Analyses a destructuring pattern applied to the content value at
200
- * `fieldPath`, recursively following each bound local variable's references
201
- * via {@link analyzeOpaqueUsage}.
202
- *
203
- * Patterns the source-code renamer cannot rewrite (rest elements, dynamic
204
- * computed keys, array patterns) mark the corresponding path opaque.
205
- */
206
- const analyzeOpaqueDestructuring = (objectPattern, scopePath, fieldPath) => {
207
- for (const property of objectPattern.properties) {
208
- if (babelTypes.isRestElement(property)) {
209
- markOpaqueField(fieldPath, property.loc?.start.line);
210
- continue;
211
- }
212
- if (!babelTypes.isObjectProperty(property)) continue;
213
- if (property.computed && !babelTypes.isStringLiteral(property.key)) {
214
- markOpaqueField(fieldPath, property.loc?.start.line);
215
- continue;
216
- }
217
- const keyName = babelTypes.isIdentifier(property.key) ? property.key.name : babelTypes.isStringLiteral(property.key) ? property.key.value : null;
218
- if (!keyName) continue;
219
- const childFieldPath = [...fieldPath, keyName];
220
- const bindingTarget = babelTypes.isAssignmentPattern(property.value) ? property.value.left : property.value;
221
- if (babelTypes.isObjectPattern(bindingTarget)) {
222
- analyzeOpaqueDestructuring(bindingTarget, scopePath, childFieldPath);
223
- continue;
224
- }
225
- if (babelTypes.isIdentifier(bindingTarget)) {
226
- const localVarBinding = scopePath.scope.getBinding(bindingTarget.name);
227
- if (!localVarBinding) continue;
228
- for (const referencePath of localVarBinding.referencePaths) analyzeOpaqueUsage(referencePath, childFieldPath);
229
- continue;
230
- }
231
- markOpaqueField(childFieldPath, property.loc?.start.line);
232
- }
233
- };
234
- /**
235
- * Helper to collect field names from an ObjectPattern (destructuring).
236
- * Returns true if successful, false when the accessed fields cannot be
237
- * determined statically (rest element or dynamic computed key → 'all').
238
- */
239
- const collectFieldsFromObjectPattern = (pattern, initPath, targetSet) => {
240
- if (pattern.properties.some((prop) => babelTypes.isRestElement(prop))) return false;
241
- for (const property of pattern.properties) {
242
- if (!babelTypes.isObjectProperty(property)) continue;
243
- if (property.computed && !babelTypes.isStringLiteral(property.key)) return false;
244
- let fieldName;
245
- if (!property.computed && babelTypes.isIdentifier(property.key)) fieldName = property.key.name;
246
- else if (babelTypes.isStringLiteral(property.key)) fieldName = property.key.value;
247
- if (!fieldName) continue;
248
- targetSet.add(fieldName);
249
- const bindingTarget = babelTypes.isAssignmentPattern(property.value) ? property.value.left : property.value;
250
- if (babelTypes.isIdentifier(bindingTarget)) {
251
- const variableBinding = initPath.scope.getBinding(bindingTarget.name);
252
- if (variableBinding) for (const refPath of variableBinding.referencePaths) analyzeOpaqueUsage(refPath, [fieldName]);
253
- } else if (babelTypes.isObjectPattern(bindingTarget)) analyzeOpaqueDestructuring(bindingTarget, initPath, [fieldName]);
254
- else markOpaqueField([fieldName], property.loc?.start.line);
255
- }
256
- return true;
257
- };
258
- /**
259
- * Returns the call path when `memberExprPath` is a chainable runtime helper
260
- * being invoked (`content.onChange(cb)`), otherwise `undefined`.
261
- *
262
- * See {@link CHAINABLE_RUNTIME_METHOD_NAMES}: a bare `content.onChange`
263
- * reference that is never called is treated as an ordinary field access, so
264
- * only the invoked form is looked through.
265
- */
266
- const getChainedRuntimeCallPath = (memberExprPath, fieldName) => {
267
- if (!CHAINABLE_RUNTIME_METHOD_NAMES.has(fieldName)) return void 0;
268
- if (!memberExprPath) return void 0;
269
- const callPath = memberExprPath.parentPath;
270
- if (!callPath) return void 0;
271
- if (!callPath.isCallExpression() && !callPath.isOptionalCallExpression()) return;
272
- if (callPath.node.callee !== memberExprPath.node) return;
273
- return callPath;
274
- };
275
- /**
276
- * Analyses the callbacks handed to a chainable runtime helper.
277
- *
278
- * The callback receives the freshly-resolved content root, so its parameter
279
- * is walked exactly like `const content = useIntlayer('key')` — either as a
280
- * plain binding or as a destructuring pattern.
281
- */
282
- const analyzeChainedCallbackArguments = (callPath) => {
283
- const argumentPaths = callPath.get("arguments");
284
- for (const argumentPath of argumentPaths) {
285
- if (!argumentPath.isArrowFunctionExpression() && !argumentPath.isFunctionExpression()) continue;
286
- const [firstParameterPath] = argumentPath.get("params");
287
- if (!firstParameterPath) continue;
288
- if (firstParameterPath.isIdentifier()) {
289
- const parameterBinding = argumentPath.scope.getBinding(firstParameterPath.node.name);
290
- if (!parameterBinding) {
291
- markUntrackedBinding();
292
- continue;
293
- }
294
- const accessedFieldNames = /* @__PURE__ */ new Set();
295
- if (analyzeContentBindingReferences(parameterBinding, accessedFieldNames)) markUntrackedBinding();
296
- else recordFieldUsage(pruneContext, dictionaryKey, accessedFieldNames);
297
- continue;
298
- }
299
- if (firstParameterPath.isObjectPattern()) {
300
- const accessedFieldNames = /* @__PURE__ */ new Set();
301
- if (collectFieldsFromObjectPattern(firstParameterPath.node, argumentPath, accessedFieldNames)) recordFieldUsage(pruneContext, dictionaryKey, accessedFieldNames);
302
- else recordFieldUsage(pruneContext, dictionaryKey, "all");
303
- continue;
304
- }
305
- markUntrackedBinding();
306
- }
307
- };
308
- /**
309
- * Walks every reference of a local binding holding the content root,
310
- * collecting the top-level fields it reads into `accessedTopLevelFieldNames`.
311
- *
312
- * @returns `true` when a reference escaped static tracking, meaning the whole
313
- * dictionary must be kept.
314
- */
315
- const analyzeContentBindingReferences = (variableBinding, accessedTopLevelFieldNames) => {
316
- for (const variableReferencePath of variableBinding.referencePaths) {
317
- const referenceParentNode = variableReferencePath.parent;
318
- if ((babelTypes.isMemberExpression(referenceParentNode) || babelTypes.isOptionalMemberExpression(referenceParentNode)) && referenceParentNode.object === variableReferencePath.node) {
319
- const memberExpressionNode = referenceParentNode;
320
- let fieldName;
321
- if (!memberExpressionNode.computed && babelTypes.isIdentifier(memberExpressionNode.property)) fieldName = memberExpressionNode.property.name;
322
- else if (memberExpressionNode.computed && babelTypes.isStringLiteral(memberExpressionNode.property)) fieldName = memberExpressionNode.property.value;
323
- if (fieldName) {
324
- const memberExprPath = variableReferencePath.parentPath;
325
- const chainedCallPath = getChainedRuntimeCallPath(memberExprPath, fieldName);
326
- if (chainedCallPath) {
327
- analyzeChainedCallbackArguments(chainedCallPath);
328
- analyzeContentRoot(chainedCallPath);
329
- continue;
330
- }
331
- accessedTopLevelFieldNames.add(fieldName);
332
- if (memberExprPath) analyzeOpaqueUsage(memberExprPath, [fieldName]);
333
- } else return true;
334
- } else if (babelTypes.isArrayExpression(referenceParentNode)) return true;
335
- else if ((babelTypes.isCallExpression(referenceParentNode) || babelTypes.isOptionalCallExpression(referenceParentNode)) && referenceParentNode.callee === variableReferencePath.node) {
336
- const callExprPath = variableReferencePath.parentPath;
337
- const callParent = callExprPath?.parent;
338
- if (callParent && (babelTypes.isMemberExpression(callParent) || babelTypes.isOptionalMemberExpression(callParent)) && callParent.object === callExprPath?.node) {
339
- const memberExpr = callParent;
340
- let fieldName;
341
- if (!memberExpr.computed && babelTypes.isIdentifier(memberExpr.property)) fieldName = memberExpr.property.name;
342
- else if (memberExpr.computed && babelTypes.isStringLiteral(memberExpr.property)) fieldName = memberExpr.property.value;
343
- if (fieldName) {
344
- const memberExprPath = callExprPath?.parentPath;
345
- const chainedCallPath = getChainedRuntimeCallPath(memberExprPath, fieldName);
346
- if (chainedCallPath) {
347
- analyzeChainedCallbackArguments(chainedCallPath);
348
- analyzeContentRoot(chainedCallPath);
349
- continue;
350
- }
351
- accessedTopLevelFieldNames.add(fieldName);
352
- if (memberExprPath) analyzeOpaqueUsage(memberExprPath, [fieldName]);
353
- } else return true;
354
- } else if (callParent && babelTypes.isVariableDeclarator(callParent) && babelTypes.isObjectPattern(callParent.id) && callExprPath && collectFieldsFromObjectPattern(callParent.id, callExprPath, accessedTopLevelFieldNames)) {} else return true;
355
- } else return true;
356
- }
357
- return false;
358
- };
359
- /**
360
- * Analyses how the content root referenced by `rootPath` is consumed.
361
- *
362
- * `rootPath` is the `useIntlayer('key')` call itself, or any expression that
363
- * evaluates back to the same content object — currently the result of a
364
- * chainable runtime helper such as `.onChange(…)`.
365
- */
366
- const analyzeContentRoot = (rootPath) => {
367
- const parentNode = rootPath.parent;
368
- if (babelTypes.isVariableDeclarator(parentNode) && babelTypes.isObjectPattern(parentNode.id)) {
369
- const accessedFieldNames = /* @__PURE__ */ new Set();
370
- if (collectFieldsFromObjectPattern(parentNode.id, rootPath, accessedFieldNames)) recordFieldUsage(pruneContext, dictionaryKey, accessedFieldNames);
371
- else recordFieldUsage(pruneContext, dictionaryKey, "all");
372
- return;
373
- }
374
- if ((babelTypes.isMemberExpression(parentNode) || babelTypes.isOptionalMemberExpression(parentNode)) && parentNode.object === rootPath.node) {
375
- let fieldName;
376
- if (!parentNode.computed && babelTypes.isIdentifier(parentNode.property)) fieldName = parentNode.property.name;
377
- else if (parentNode.computed && babelTypes.isStringLiteral(parentNode.property)) fieldName = parentNode.property.value;
378
- if (fieldName) {
379
- const memberExprPath = rootPath.parentPath;
380
- const chainedCallPath = getChainedRuntimeCallPath(memberExprPath, fieldName);
381
- if (chainedCallPath) {
382
- analyzeChainedCallbackArguments(chainedCallPath);
383
- analyzeContentRoot(chainedCallPath);
384
- return;
385
- }
386
- recordFieldUsage(pruneContext, dictionaryKey, /* @__PURE__ */ new Set([fieldName]));
387
- if (memberExprPath) analyzeOpaqueUsage(memberExprPath, [fieldName]);
388
- } else markUntrackedBinding();
389
- return;
390
- }
391
- if (babelTypes.isVariableDeclarator(parentNode) && babelTypes.isIdentifier(parentNode.id)) {
392
- const variableName = parentNode.id.name;
393
- const variableBinding = rootPath.scope.getBinding(variableName);
394
- if (!variableBinding) {
395
- markUntrackedBinding();
396
- return;
397
- }
398
- const accessedTopLevelFieldNames = /* @__PURE__ */ new Set();
399
- if (analyzeContentBindingReferences(variableBinding, accessedTopLevelFieldNames)) markUntrackedBinding();
400
- else if (isSfcFile) deferFrameworkAnalysis(variableName);
401
- else if (variableBinding.referencePaths.length === 0) markUntrackedBinding();
402
- else recordFieldUsage(pruneContext, dictionaryKey, accessedTopLevelFieldNames);
403
- return;
404
- }
405
- if (babelTypes.isExpressionStatement(parentNode)) return;
406
- markUntrackedBinding();
407
- };
408
- analyzeContentRoot(unwrapAwait(babelTypes, callExpressionPath));
409
- };
410
- /**
411
- * Reads a fully-static string from an AST node. Returns `undefined` for
412
- * dynamic values (identifiers, expressions, template literals with
413
- * interpolations, …).
414
- */
415
- const readStaticString = (babelTypes, node) => {
416
- if (!node) return void 0;
417
- if (babelTypes.isStringLiteral(node)) return node.value;
418
- if (babelTypes.isTemplateLiteral(node) && node.expressions.length === 0 && node.quasis.length === 1) return node.quasis[0]?.value.cooked ?? node.quasis[0]?.value.raw;
419
- };
420
- /** Returns the first dot-path segment of a key, e.g. `'a.b.c'` → `'a'`. */
421
- const firstPathSegment = (path) => path.split(".")[0] ?? path;
422
- /**
423
- * Reads the static first dot-path segment from a `t()` first-argument node.
424
- *
425
- * t('counter.label') → 'counter'
426
- * t(`counter.${x}`) → 'counter' (static prefix before the first dot)
427
- * t(`${x}.label`) → undefined (dynamic first segment)
428
- * t(someVariable) → undefined
429
- */
430
- const readStaticFirstSegment = (babelTypes, node) => {
431
- const staticString = readStaticString(babelTypes, node);
432
- if (staticString !== void 0) return firstPathSegment(staticString);
433
- if (babelTypes.isTemplateLiteral(node) && node.quasis.length > 0) {
434
- const firstQuasi = node.quasis[0]?.value.cooked ?? node.quasis[0]?.value.raw;
435
- if (firstQuasi?.includes(".")) return firstPathSegment(firstQuasi);
436
- }
437
- };
438
- /**
439
- * Reads a static string property from an object expression. Returns
440
- * `'__default__'` when the property is absent and `undefined` when present but
441
- * dynamic.
442
- */
443
- const readObjectProperty = (babelTypes, objectExpression, propertyName) => {
444
- for (const property of objectExpression.properties) {
445
- if (!babelTypes.isObjectProperty(property)) continue;
446
- if (!(babelTypes.isIdentifier(property.key) && property.key.name === propertyName || babelTypes.isStringLiteral(property.key) && property.key.value === propertyName)) continue;
447
- return readStaticString(babelTypes, property.value) ?? void 0;
448
- }
449
- return "__default__";
450
- };
451
- /**
452
- * Reads a named JSX attribute as a static string and returns it wrapped in a
453
- * `StringLiteral` node so the result can be fed to the same namespace resolver
454
- * as a call argument. Handles both `id="home.title"` and `id={'home.title'}`.
455
- * Returns `undefined` when the attribute is absent or dynamic (`id={expr}`).
456
- */
457
- const readJsxAttributeString = (babelTypes, openingElement, attributeName) => {
458
- for (const attribute of openingElement.attributes) {
459
- if (!babelTypes.isJSXAttribute(attribute)) continue;
460
- if (!babelTypes.isJSXIdentifier(attribute.name) || attribute.name.name !== attributeName) continue;
461
- const value = attribute.value;
462
- if (babelTypes.isStringLiteral(value)) return value;
463
- if (babelTypes.isJSXExpressionContainer(value)) {
464
- const staticString = readStaticString(babelTypes, value.expression);
465
- if (staticString !== void 0) return babelTypes.stringLiteral(staticString);
466
- }
467
- return;
468
- }
469
- };
470
- /**
471
- * Resolves the namespace (dictionary key) for a compat caller call-site from
472
- * one `CallerValueSource`. Returns the static key, or `'__default__'` when the
473
- * configured argument is absent (caller falls back to its default namespace),
474
- * or `undefined` when the value is present but dynamic.
475
- */
476
- const resolveCompatNamespace = (babelTypes, callArguments, source) => {
477
- if (source.from === "fixed") return source.value;
478
- if (source.from === "path-first-segment") {
479
- const firstArg = callArguments[0];
480
- if (firstArg === void 0) return void 0;
481
- const staticString = readStaticString(babelTypes, firstArg);
482
- if (staticString !== void 0) return firstPathSegment(staticString);
483
- if (babelTypes.isObjectExpression(firstArg)) {
484
- const idValue = readObjectProperty(babelTypes, firstArg, "id");
485
- if (idValue === "__default__") return "__default__";
486
- if (idValue !== void 0) return firstPathSegment(idValue);
487
- return;
488
- }
489
- return;
490
- }
491
- if (source.from === "argument") {
492
- const argument = callArguments[source.index];
493
- if (argument === void 0) return "__default__";
494
- const staticString = readStaticString(babelTypes, argument);
495
- if (staticString !== void 0) return staticString;
496
- if (babelTypes.isObjectExpression(argument)) return readObjectProperty(babelTypes, argument, "namespace");
497
- return;
498
- }
499
- const optionsArgument = callArguments[source.argumentIndex];
500
- if (optionsArgument === void 0) return "__default__";
501
- if (!babelTypes.isObjectExpression(optionsArgument)) return void 0;
502
- return readObjectProperty(babelTypes, optionsArgument, source.property);
503
- };
504
- /**
505
- * Resolves the namespace from a list of `CallerValueSource`s, tried in
506
- * declaration order. The first source yielding a static string wins.
507
- * Returns `'__default__'` when every source reports an absent value, and
508
- * `undefined` when the namespace is present somewhere but dynamic.
509
- */
510
- const resolveNamespaceFromSources = (babelTypes, callArguments, sources) => {
511
- let sawAbsentValue = false;
512
- for (const source of sources) {
513
- const resolved = resolveCompatNamespace(babelTypes, callArguments, source);
514
- if (typeof resolved === "string" && resolved !== "__default__") return resolved;
515
- if (resolved === "__default__") sawAbsentValue = true;
516
- }
517
- return sawAbsentValue ? "__default__" : void 0;
518
- };
519
- /**
520
- * Resolves an optional `keyPrefix` for a compat caller. Returns the static
521
- * prefix string, `null` when no prefix is configured/present, or `undefined`
522
- * when a prefix is present but dynamic.
523
- */
524
- const resolveCompatKeyPrefix = (babelTypes, callArguments, sources) => {
525
- if (!sources || sources.length === 0) return null;
526
- const resolved = resolveNamespaceFromSources(babelTypes, callArguments, sources);
527
- if (resolved === "__default__") return null;
528
- return resolved;
529
- };
530
- /**
531
- * Analyses how the translation function produced by a compat namespace caller
532
- * (`useTranslation`, `useTranslations`, `getTranslations`, `getFixedT`,
533
- * `useI18n`) is consumed, then records the accessed top-level dictionary fields
534
- * into `pruneContext`.
535
- *
536
- * Dictionaries consumed this way are always added to
537
- * `dictionariesSkippingFieldRename`: the field accesses are string-literal
538
- * dot-paths inside `t()` calls, which the field-rename plugin cannot rewrite,
539
- * so renaming the compiled JSON keys would break runtime lookups. Pruning
540
- * (top-level field removal) remains safe because it preserves field names.
541
- */
542
- const analyzeNamespaceCallerUsage = (babelTypes, pruneContext, callArguments, callerConfig, isSfcFile, currentSourceFilePath, callExpressionPath, namespaceOverride) => {
543
- const resolvedNamespace = namespaceOverride ?? resolveNamespaceFromSources(babelTypes, callArguments, callerConfig.namespaceSources);
544
- if (resolvedNamespace === void 0) return;
545
- const namespaceString = resolvedNamespace === "__default__" ? DEFAULT_COMPAT_NAMESPACE : resolvedNamespace;
546
- const namespaceSegments = namespaceString.split(".");
547
- const dictionaryKey = namespaceSegments[0] ?? namespaceString;
548
- const namespacePrefix = namespaceSegments.length > 1 ? namespaceSegments.slice(1).join(".") : null;
549
- pruneContext.dictionariesSkippingFieldRename.add(dictionaryKey);
550
- if (isSfcFile) {
551
- recordFieldUsage(pruneContext, dictionaryKey, "all");
552
- return;
553
- }
554
- if (callerConfig.translationFunction === "all") {
555
- recordFieldUsage(pruneContext, dictionaryKey, "all");
556
- return;
557
- }
558
- if (callerConfig.translationFunction === "content") {
559
- if (callExpressionPath) analyzeCallExpressionUsage(babelTypes, pruneContext, callExpressionPath, dictionaryKey, currentSourceFilePath, isSfcFile);
560
- else recordFieldUsage(pruneContext, dictionaryKey, "all");
561
- return;
562
- }
563
- if (callerConfig.translationFunction === "self") {
564
- const firstArg = callArguments[0];
565
- if (!firstArg) {
566
- recordFieldUsage(pruneContext, dictionaryKey, "all");
567
- return;
568
- }
569
- if (callerConfig.namespaceSources.some((source) => source.from === "path-first-segment")) {
570
- let fullId;
571
- const staticString = readStaticString(babelTypes, firstArg);
572
- if (staticString !== void 0) fullId = staticString;
573
- else if (babelTypes.isObjectExpression(firstArg)) {
574
- const idValue = readObjectProperty(babelTypes, firstArg, "id");
575
- if (idValue !== void 0 && idValue !== "__default__") fullId = idValue;
576
- }
577
- if (fullId !== void 0) {
578
- const field = fullId.split(".")[1];
579
- if (field !== void 0) recordFieldUsage(pruneContext, dictionaryKey, /* @__PURE__ */ new Set([field]));
580
- else recordFieldUsage(pruneContext, dictionaryKey, "all");
581
- return;
582
- }
583
- recordFieldUsage(pruneContext, dictionaryKey, "all");
584
- return;
585
- }
586
- const segment = callerConfig.flatKey ? readStaticString(babelTypes, firstArg) : readStaticFirstSegment(babelTypes, firstArg);
587
- if (segment !== void 0) {
588
- recordFieldUsage(pruneContext, dictionaryKey, /* @__PURE__ */ new Set([segment]));
589
- return;
590
- }
591
- if (babelTypes.isObjectExpression(firstArg)) {
592
- const idValue = readObjectProperty(babelTypes, firstArg, "id");
593
- if (idValue !== void 0 && idValue !== "__default__") {
594
- recordFieldUsage(pruneContext, dictionaryKey, /* @__PURE__ */ new Set([callerConfig.flatKey ? idValue : firstPathSegment(idValue)]));
595
- return;
596
- }
597
- }
598
- recordFieldUsage(pruneContext, dictionaryKey, "all");
599
- return;
600
- }
601
- if (!callExpressionPath) {
602
- recordFieldUsage(pruneContext, dictionaryKey, "all");
603
- return;
604
- }
605
- const explicitKeyPrefix = resolveCompatKeyPrefix(babelTypes, callArguments, callerConfig.keyPrefixSources);
606
- if (explicitKeyPrefix === void 0) {
607
- recordFieldUsage(pruneContext, dictionaryKey, "all");
608
- return;
609
- }
610
- const effectivePrefix = namespacePrefix ?? explicitKeyPrefix;
611
- if (effectivePrefix !== null) {
612
- recordFieldUsage(pruneContext, dictionaryKey, /* @__PURE__ */ new Set([firstPathSegment(effectivePrefix)]));
613
- return;
614
- }
615
- let translationBinding = null;
616
- if (callerConfig.translationFunction === "destructured-t") {
617
- const parentNode = callExpressionPath.parent;
618
- if (babelTypes.isVariableDeclarator(parentNode) && babelTypes.isObjectPattern(parentNode.id)) {
619
- for (const property of parentNode.id.properties) if (babelTypes.isObjectProperty(property) && babelTypes.isIdentifier(property.key) && property.key.name === "t" && babelTypes.isIdentifier(property.value)) translationBinding = callExpressionPath.scope.getBinding(property.value.name) ?? null;
620
- }
621
- } else {
622
- const parentNode = unwrapAwait(babelTypes, callExpressionPath).parent;
623
- if (babelTypes.isVariableDeclarator(parentNode) && babelTypes.isIdentifier(parentNode.id)) translationBinding = callExpressionPath.scope.getBinding(parentNode.id.name) ?? null;
624
- }
625
- if (!translationBinding) {
626
- recordFieldUsage(pruneContext, dictionaryKey, "all");
627
- return;
628
- }
629
- const accessedFields = /* @__PURE__ */ new Set();
630
- let hasUntrackedUsage = false;
631
- for (const referencePath of translationBinding.referencePaths) {
632
- const parentNode = referencePath.parent;
633
- if (!((babelTypes.isCallExpression(parentNode) || babelTypes.isOptionalCallExpression(parentNode)) && parentNode.callee === referencePath.node)) {
634
- hasUntrackedUsage = true;
635
- break;
636
- }
637
- const firstArgument = parentNode.arguments[0];
638
- const segment = readStaticFirstSegment(babelTypes, firstArgument);
639
- if (segment === void 0) {
640
- hasUntrackedUsage = true;
641
- break;
642
- }
643
- accessedFields.add(segment);
644
- }
645
- if (hasUntrackedUsage) {
646
- recordFieldUsage(pruneContext, dictionaryKey, "all");
647
- return;
648
- }
649
- if (accessedFields.size > 0) recordFieldUsage(pruneContext, dictionaryKey, accessedFields);
650
- };
651
14
  /**
652
15
  * Creates a Babel plugin that traverses source files and records which
653
- * top-level dictionary fields each `useIntlayer` / `getIntlayer` call-site —
654
- * and each configured compat namespace caller — accesses. Results are
655
- * accumulated into `pruneContext`.
16
+ * top-level dictionary fields each `useIntlayer` / `getIntlayer` call-site
17
+ * accesses. Results are accumulated into `pruneContext`.
18
+ *
19
+ * Compat-adapter callers (`useTranslation`, `useTranslations`, `formatMessage`,
20
+ * …) are *not* known to this plugin. Each compat package's bundler plugin
21
+ * injects its own descriptors through `options.compatCallers`, and the whole
22
+ * compat analysis then runs behind {@link createCompatUsageAnalyzer}. With no
23
+ * descriptors injected — the default — none of that code is reached, so a
24
+ * plain intlayer build behaves exactly as if the adapters did not exist.
656
25
  *
657
26
  * This plugin is analysis-only: it does not transform the code (`code: false`
658
27
  * should be passed to `transformAsync` when using it).
659
28
  *
660
29
  * @param pruneContext - Shared mutable state written by this plugin.
661
- * @param options - Optional overrides. `compatCallers` defaults to
662
- * {@link DEFAULT_COMPAT_CALLERS}; pass `[]` to disable
663
- * compat-adapter analysis entirely.
30
+ * @param options - Optional overrides. `compatCallers` defaults to an
31
+ * empty registry, disabling compat-adapter analysis.
664
32
  */
665
33
  const makeUsageAnalyzerBabelPlugin = (pruneContext, options) => ({ types: babelTypes }) => {
666
- const compatCallers = options?.compatCallers ?? DEFAULT_COMPAT_CALLERS;
34
+ const compatCallers = options?.compatCallers ?? [];
667
35
  return {
668
36
  name: "intlayer-usage-analyzer",
669
37
  visitor: { Program: { exit: (programPath, state) => {
670
- const currentSourceFilePath = state.file.opts.filename ?? "unknown file";
671
- const isSfcFile = currentSourceFilePath.endsWith(".vue") || currentSourceFilePath.endsWith(".svelte") || currentSourceFilePath.endsWith(".astro");
672
- const intlayerCallerLocalNameMap = /* @__PURE__ */ new Map();
673
- const compatCallerLocalNameMap = /* @__PURE__ */ new Map();
674
- const fileImportSources = /* @__PURE__ */ new Set();
675
- programPath.traverse({ ImportDeclaration: (importDeclarationPath) => {
676
- const importSource = importDeclarationPath.node.source.value;
677
- fileImportSources.add(importSource);
678
- for (const importSpecifier of importDeclarationPath.node.specifiers) {
38
+ const sourceFilePath = state.file.opts.filename ?? "unknown file";
39
+ const isSfcFile = sourceFilePath.endsWith(".vue") || sourceFilePath.endsWith(".svelte") || sourceFilePath.endsWith(".astro");
40
+ const compatAnalyzer = require_compat_usageAnalysis.createCompatUsageAnalyzer(babelTypes, pruneContext, compatCallers, {
41
+ sourceFilePath,
42
+ isSfcFile
43
+ });
44
+ const nativeCallerLocalNames = /* @__PURE__ */ new Set();
45
+ for (const statement of programPath.node.body) {
46
+ if (!babelTypes.isImportDeclaration(statement)) continue;
47
+ const importSource = statement.source.value;
48
+ for (const importSpecifier of statement.specifiers) {
679
49
  if (!babelTypes.isImportSpecifier(importSpecifier)) continue;
680
50
  const importedName = babelTypes.isIdentifier(importSpecifier.imported) ? importSpecifier.imported.name : importSpecifier.imported.value;
681
- if (INTLAYER_CALLER_NAMES.includes(importedName)) {
682
- intlayerCallerLocalNameMap.set(importSpecifier.local.name, importedName);
51
+ const localName = importSpecifier.local.name;
52
+ if (require_nativeCallers.NATIVE_CALLER_NAME_SET.has(importedName)) {
53
+ nativeCallerLocalNames.add(localName);
683
54
  continue;
684
55
  }
685
- const compatCaller = compatCallers.find((caller) => caller.callerName === importedName && caller.importSources.includes(importSource));
686
- if (compatCaller) compatCallerLocalNameMap.set(importSpecifier.local.name, compatCaller);
56
+ compatAnalyzer?.noteImport(importSource, importedName, localName);
687
57
  }
688
- } });
689
- const methodCompatCallers = compatCallers.filter((caller) => caller.matchAsMethod && (!caller.requiresImport || caller.importSources.some((source) => fileImportSources.has(source))));
690
- const hasNativeCallers = intlayerCallerLocalNameMap.size > 0;
691
- const hasCompatCallers = compatCallerLocalNameMap.size > 0 || methodCompatCallers.length > 0;
692
- if (!hasNativeCallers && !hasCompatCallers) return;
58
+ }
59
+ const hasCompatCallers = compatAnalyzer?.hasMatchableCallers() ?? false;
60
+ if (nativeCallerLocalNames.size === 0 && !hasCompatCallers) return;
693
61
  programPath.traverse({
694
62
  CallExpression: (callExpressionPath) => {
695
63
  const calleeNode = callExpressionPath.node.callee;
@@ -701,37 +69,18 @@ const makeUsageAnalyzerBabelPlugin = (pruneContext, options) => ({ types: babelT
701
69
  isMethodCall = true;
702
70
  }
703
71
  if (!localCallerName) return;
704
- if (intlayerCallerLocalNameMap.has(localCallerName)) {
72
+ if (nativeCallerLocalNames.has(localCallerName)) {
705
73
  const callArguments = callExpressionPath.node.arguments;
706
74
  if (callArguments.length === 0) return;
707
- const dictionaryKey = readStaticString(babelTypes, callArguments[0]);
75
+ const dictionaryKey = require_staticAstReaders.readStaticString(babelTypes, callArguments[0]);
708
76
  if (!dictionaryKey) return;
709
- analyzeCallExpressionUsage(babelTypes, pruneContext, callExpressionPath, dictionaryKey, currentSourceFilePath, isSfcFile);
710
- return;
711
- }
712
- const importedCompatCaller = compatCallerLocalNameMap.get(localCallerName);
713
- if (importedCompatCaller && !isMethodCall) {
714
- analyzeNamespaceCallerUsage(babelTypes, pruneContext, callExpressionPath.node.arguments, importedCompatCaller, isSfcFile, currentSourceFilePath, callExpressionPath);
77
+ require_analyzeContentUsage.analyzeCallExpressionUsage(babelTypes, pruneContext, callExpressionPath, dictionaryKey, sourceFilePath, isSfcFile);
715
78
  return;
716
79
  }
717
- if (isMethodCall) {
718
- const methodCaller = methodCompatCallers.find((caller) => caller.callerName === localCallerName);
719
- if (methodCaller) analyzeNamespaceCallerUsage(babelTypes, pruneContext, callExpressionPath.node.arguments, methodCaller, isSfcFile, currentSourceFilePath, callExpressionPath);
720
- }
80
+ compatAnalyzer?.analyzeCall(callExpressionPath, localCallerName, isMethodCall);
721
81
  },
722
82
  JSXOpeningElement: (jsxOpeningElementPath) => {
723
- const nameNode = jsxOpeningElementPath.node.name;
724
- if (!babelTypes.isJSXIdentifier(nameNode)) return;
725
- const jsxCaller = compatCallerLocalNameMap.get(nameNode.name);
726
- if (!jsxCaller?.jsxIdAttribute) return;
727
- const idNode = readJsxAttributeString(babelTypes, jsxOpeningElementPath.node, jsxCaller.jsxIdAttribute);
728
- let namespaceOverride;
729
- if (jsxCaller.jsxNamespaceAttribute) {
730
- const namespaceNode = readJsxAttributeString(babelTypes, jsxOpeningElementPath.node, jsxCaller.jsxNamespaceAttribute);
731
- if (!namespaceNode) return;
732
- namespaceOverride = namespaceNode.value;
733
- }
734
- analyzeNamespaceCallerUsage(babelTypes, pruneContext, idNode ? [idNode] : [], jsxCaller, isSfcFile, currentSourceFilePath, void 0, namespaceOverride);
83
+ compatAnalyzer?.analyzeJsxElement(jsxOpeningElementPath);
735
84
  }
736
85
  });
737
86
  } } }
@@ -739,11 +88,12 @@ const makeUsageAnalyzerBabelPlugin = (pruneContext, options) => ({ types: babelT
739
88
  };
740
89
 
741
90
  //#endregion
742
- exports.CHAINABLE_RUNTIME_METHOD_NAMES = CHAINABLE_RUNTIME_METHOD_NAMES;
91
+ exports.CHAINABLE_RUNTIME_METHOD_NAMES = require_nativeCallers.CHAINABLE_RUNTIME_METHOD_NAMES;
743
92
  exports.DEFAULT_COMPAT_CALLERS = DEFAULT_COMPAT_CALLERS;
744
- exports.INTLAYER_CALLER_NAMES = INTLAYER_CALLER_NAMES;
745
- exports.createPruneContext = createPruneContext;
93
+ exports.INTLAYER_CALLER_NAMES = require_nativeCallers.INTLAYER_CALLER_NAMES;
94
+ exports.NATIVE_CALLER_NAME_SET = require_nativeCallers.NATIVE_CALLER_NAME_SET;
95
+ exports.createPruneContext = require_pruneContext.createPruneContext;
746
96
  exports.makeUsageAnalyzerBabelPlugin = makeUsageAnalyzerBabelPlugin;
747
- exports.preserveNestedDictionaryFields = preserveNestedDictionaryFields;
748
- exports.unwrapAwait = unwrapAwait;
97
+ exports.preserveNestedDictionaryFields = require_pruneContext.preserveNestedDictionaryFields;
98
+ exports.unwrapAwait = require_staticAstReaders.unwrapAwait;
749
99
  //# sourceMappingURL=babel-plugin-intlayer-usage-analyzer.cjs.map