@intlayer/babel 9.3.1 → 9.3.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.
Files changed (43) hide show
  1. package/dist/cjs/babel-plugin-intlayer-field-rename.cjs +66 -8
  2. package/dist/cjs/babel-plugin-intlayer-field-rename.cjs.map +1 -1
  3. package/dist/cjs/babel-plugin-intlayer-usage-analyzer.cjs +142 -53
  4. package/dist/cjs/babel-plugin-intlayer-usage-analyzer.cjs.map +1 -1
  5. package/dist/cjs/extractContent/babelProcessor.cjs +7 -8
  6. package/dist/cjs/extractContent/babelProcessor.cjs.map +1 -1
  7. package/dist/cjs/extractContent/extractContent.cjs +3 -4
  8. package/dist/cjs/extractContent/extractContent.cjs.map +1 -1
  9. package/dist/cjs/extractContent/index.cjs +0 -1
  10. package/dist/cjs/extractContent/utils/constants.cjs +0 -13
  11. package/dist/cjs/extractContent/utils/constants.cjs.map +1 -1
  12. package/dist/cjs/extractContent/utils/index.cjs +1 -4
  13. package/dist/cjs/index.cjs +1 -1
  14. package/dist/esm/babel-plugin-intlayer-field-rename.mjs +67 -9
  15. package/dist/esm/babel-plugin-intlayer-field-rename.mjs.map +1 -1
  16. package/dist/esm/babel-plugin-intlayer-usage-analyzer.mjs +142 -54
  17. package/dist/esm/babel-plugin-intlayer-usage-analyzer.mjs.map +1 -1
  18. package/dist/esm/extractContent/babelProcessor.mjs +1 -2
  19. package/dist/esm/extractContent/babelProcessor.mjs.map +1 -1
  20. package/dist/esm/extractContent/extractContent.mjs +1 -2
  21. package/dist/esm/extractContent/extractContent.mjs.map +1 -1
  22. package/dist/esm/extractContent/index.mjs +2 -2
  23. package/dist/esm/extractContent/utils/constants.mjs +1 -13
  24. package/dist/esm/extractContent/utils/constants.mjs.map +1 -1
  25. package/dist/esm/extractContent/utils/index.mjs +2 -3
  26. package/dist/esm/index.mjs +3 -3
  27. package/dist/types/babel-plugin-intlayer-field-rename.d.ts.map +1 -1
  28. package/dist/types/babel-plugin-intlayer-usage-analyzer.d.ts +16 -1
  29. package/dist/types/babel-plugin-intlayer-usage-analyzer.d.ts.map +1 -1
  30. package/dist/types/extractContent/babelProcessor.d.ts.map +1 -1
  31. package/dist/types/extractContent/extractContent.d.ts.map +1 -1
  32. package/dist/types/extractContent/index.d.ts +2 -2
  33. package/dist/types/extractContent/utils/constants.d.ts +1 -7
  34. package/dist/types/extractContent/utils/constants.d.ts.map +1 -1
  35. package/dist/types/extractContent/utils/index.d.ts +2 -3
  36. package/dist/types/index.d.ts +3 -3
  37. package/package.json +9 -9
  38. package/dist/cjs/extractContent/utils/shouldExtract.cjs +0 -38
  39. package/dist/cjs/extractContent/utils/shouldExtract.cjs.map +0 -1
  40. package/dist/esm/extractContent/utils/shouldExtract.mjs +0 -37
  41. package/dist/esm/extractContent/utils/shouldExtract.mjs.map +0 -1
  42. package/dist/types/extractContent/utils/shouldExtract.d.ts +0 -15
  43. package/dist/types/extractContent/utils/shouldExtract.d.ts.map +0 -1
@@ -53,6 +53,21 @@ const preserveNestedDictionaryFields = (pruneContext, nestedDictionaryReferences
53
53
  for (const nestedField of nestedFields) currentFieldUsage.add(nestedField);
54
54
  }
55
55
  };
56
+ /**
57
+ * Methods the runtime attaches to a resolved content object rather than fields
58
+ * declared in a `.content` file.
59
+ *
60
+ * `vanilla-intlayer`'s `useIntlayer` / `useDictionary` assign `onChange` onto
61
+ * the content they return, so `useIntlayer('app').onChange(cb)` is a
62
+ * subscription — not a read of a field named `onChange`. Without this list the
63
+ * analyser records `onChange` as the only consumed field and the purge pass
64
+ * strips every real field from the compiled dictionary.
65
+ *
66
+ * The analyser looks *through* these calls: the callback parameter is analysed
67
+ * as a fresh content root, and the call result is analysed again because the
68
+ * helpers return the content object for chaining.
69
+ */
70
+ const CHAINABLE_RUNTIME_METHOD_NAMES = /* @__PURE__ */ new Set(["onChange"]);
56
71
  /** Canonical intlayer caller names that trigger usage analysis. */
57
72
  const INTLAYER_CALLER_NAMES = ["useIntlayer", "getIntlayer"];
58
73
  /**
@@ -97,7 +112,6 @@ const recordFieldUsage = (pruneContext, dictionaryKey, fieldUsage) => {
97
112
  * const result = useIntlayer('key') → records 'all' (untracked binding)
98
113
  */
99
114
  const analyzeCallExpressionUsage = (babelTypes, pruneContext, callExpressionPath, dictionaryKey, currentSourceFilePath, isSfcFile) => {
100
- const parentNode = callExpressionPath.parent;
101
115
  /** Mark the dictionary key as having an untracked binding in this file. */
102
116
  const markUntrackedBinding = () => {
103
117
  const existingPaths = pruneContext.dictionaryKeysWithUntrackedBindings.get(dictionaryKey) ?? [];
@@ -225,32 +239,64 @@ const analyzeCallExpressionUsage = (babelTypes, pruneContext, callExpressionPath
225
239
  }
226
240
  return true;
227
241
  };
228
- if (babelTypes.isVariableDeclarator(parentNode) && babelTypes.isObjectPattern(parentNode.id)) {
229
- const accessedFieldNames = /* @__PURE__ */ new Set();
230
- if (collectFieldsFromObjectPattern(parentNode.id, callExpressionPath, accessedFieldNames)) recordFieldUsage(pruneContext, dictionaryKey, accessedFieldNames);
231
- else recordFieldUsage(pruneContext, dictionaryKey, "all");
232
- return;
233
- }
234
- if ((babelTypes.isMemberExpression(parentNode) || babelTypes.isOptionalMemberExpression(parentNode)) && parentNode.object === callExpressionPath.node) {
235
- let fieldName;
236
- if (!parentNode.computed && babelTypes.isIdentifier(parentNode.property)) fieldName = parentNode.property.name;
237
- else if (parentNode.computed && babelTypes.isStringLiteral(parentNode.property)) fieldName = parentNode.property.value;
238
- if (fieldName) {
239
- recordFieldUsage(pruneContext, dictionaryKey, /* @__PURE__ */ new Set([fieldName]));
240
- const memberExprPath = callExpressionPath.parentPath;
241
- if (memberExprPath) analyzeOpaqueUsage(memberExprPath, [fieldName]);
242
- } else markUntrackedBinding();
243
- return;
244
- }
245
- if (babelTypes.isVariableDeclarator(parentNode) && babelTypes.isIdentifier(parentNode.id)) {
246
- const variableName = parentNode.id.name;
247
- const variableBinding = callExpressionPath.scope.getBinding(variableName);
248
- if (!variableBinding) {
242
+ /**
243
+ * Returns the call path when `memberExprPath` is a chainable runtime helper
244
+ * being invoked (`content.onChange(cb)`), otherwise `undefined`.
245
+ *
246
+ * See {@link CHAINABLE_RUNTIME_METHOD_NAMES}: a bare `content.onChange`
247
+ * reference that is never called is treated as an ordinary field access, so
248
+ * only the invoked form is looked through.
249
+ */
250
+ const getChainedRuntimeCallPath = (memberExprPath, fieldName) => {
251
+ if (!CHAINABLE_RUNTIME_METHOD_NAMES.has(fieldName)) return void 0;
252
+ if (!memberExprPath) return void 0;
253
+ const callPath = memberExprPath.parentPath;
254
+ if (!callPath) return void 0;
255
+ if (!callPath.isCallExpression() && !callPath.isOptionalCallExpression()) return;
256
+ if (callPath.node.callee !== memberExprPath.node) return;
257
+ return callPath;
258
+ };
259
+ /**
260
+ * Analyses the callbacks handed to a chainable runtime helper.
261
+ *
262
+ * The callback receives the freshly-resolved content root, so its parameter
263
+ * is walked exactly like `const content = useIntlayer('key')` — either as a
264
+ * plain binding or as a destructuring pattern.
265
+ */
266
+ const analyzeChainedCallbackArguments = (callPath) => {
267
+ const argumentPaths = callPath.get("arguments");
268
+ for (const argumentPath of argumentPaths) {
269
+ if (!argumentPath.isArrowFunctionExpression() && !argumentPath.isFunctionExpression()) continue;
270
+ const [firstParameterPath] = argumentPath.get("params");
271
+ if (!firstParameterPath) continue;
272
+ if (firstParameterPath.isIdentifier()) {
273
+ const parameterBinding = argumentPath.scope.getBinding(firstParameterPath.node.name);
274
+ if (!parameterBinding) {
275
+ markUntrackedBinding();
276
+ continue;
277
+ }
278
+ const accessedFieldNames = /* @__PURE__ */ new Set();
279
+ if (analyzeContentBindingReferences(parameterBinding, accessedFieldNames)) markUntrackedBinding();
280
+ else recordFieldUsage(pruneContext, dictionaryKey, accessedFieldNames);
281
+ continue;
282
+ }
283
+ if (firstParameterPath.isObjectPattern()) {
284
+ const accessedFieldNames = /* @__PURE__ */ new Set();
285
+ if (collectFieldsFromObjectPattern(firstParameterPath.node, argumentPath, accessedFieldNames)) recordFieldUsage(pruneContext, dictionaryKey, accessedFieldNames);
286
+ else recordFieldUsage(pruneContext, dictionaryKey, "all");
287
+ continue;
288
+ }
249
289
  markUntrackedBinding();
250
- return;
251
290
  }
252
- const accessedTopLevelFieldNames = /* @__PURE__ */ new Set();
253
- let hasUntrackedReferenceAccess = false;
291
+ };
292
+ /**
293
+ * Walks every reference of a local binding holding the content root,
294
+ * collecting the top-level fields it reads into `accessedTopLevelFieldNames`.
295
+ *
296
+ * @returns `true` when a reference escaped static tracking, meaning the whole
297
+ * dictionary must be kept.
298
+ */
299
+ const analyzeContentBindingReferences = (variableBinding, accessedTopLevelFieldNames) => {
254
300
  for (const variableReferencePath of variableBinding.referencePaths) {
255
301
  const referenceParentNode = variableReferencePath.parent;
256
302
  if ((babelTypes.isMemberExpression(referenceParentNode) || babelTypes.isOptionalMemberExpression(referenceParentNode)) && referenceParentNode.object === variableReferencePath.node) {
@@ -259,17 +305,18 @@ const analyzeCallExpressionUsage = (babelTypes, pruneContext, callExpressionPath
259
305
  if (!memberExpressionNode.computed && babelTypes.isIdentifier(memberExpressionNode.property)) fieldName = memberExpressionNode.property.name;
260
306
  else if (memberExpressionNode.computed && babelTypes.isStringLiteral(memberExpressionNode.property)) fieldName = memberExpressionNode.property.value;
261
307
  if (fieldName) {
262
- accessedTopLevelFieldNames.add(fieldName);
263
308
  const memberExprPath = variableReferencePath.parentPath;
309
+ const chainedCallPath = getChainedRuntimeCallPath(memberExprPath, fieldName);
310
+ if (chainedCallPath) {
311
+ analyzeChainedCallbackArguments(chainedCallPath);
312
+ analyzeContentRoot(chainedCallPath);
313
+ continue;
314
+ }
315
+ accessedTopLevelFieldNames.add(fieldName);
264
316
  if (memberExprPath) analyzeOpaqueUsage(memberExprPath, [fieldName]);
265
- } else {
266
- hasUntrackedReferenceAccess = true;
267
- break;
268
- }
269
- } else if (babelTypes.isArrayExpression(referenceParentNode)) {
270
- hasUntrackedReferenceAccess = true;
271
- break;
272
- } else if ((babelTypes.isCallExpression(referenceParentNode) || babelTypes.isOptionalCallExpression(referenceParentNode)) && referenceParentNode.callee === variableReferencePath.node) {
317
+ } else return true;
318
+ } else if (babelTypes.isArrayExpression(referenceParentNode)) return true;
319
+ else if ((babelTypes.isCallExpression(referenceParentNode) || babelTypes.isOptionalCallExpression(referenceParentNode)) && referenceParentNode.callee === variableReferencePath.node) {
273
320
  const callExprPath = variableReferencePath.parentPath;
274
321
  const callParent = callExprPath?.parent;
275
322
  if (callParent && (babelTypes.isMemberExpression(callParent) || babelTypes.isOptionalMemberExpression(callParent)) && callParent.object === callExprPath?.node) {
@@ -278,30 +325,71 @@ const analyzeCallExpressionUsage = (babelTypes, pruneContext, callExpressionPath
278
325
  if (!memberExpr.computed && babelTypes.isIdentifier(memberExpr.property)) fieldName = memberExpr.property.name;
279
326
  else if (memberExpr.computed && babelTypes.isStringLiteral(memberExpr.property)) fieldName = memberExpr.property.value;
280
327
  if (fieldName) {
281
- accessedTopLevelFieldNames.add(fieldName);
282
328
  const memberExprPath = callExprPath?.parentPath;
329
+ const chainedCallPath = getChainedRuntimeCallPath(memberExprPath, fieldName);
330
+ if (chainedCallPath) {
331
+ analyzeChainedCallbackArguments(chainedCallPath);
332
+ analyzeContentRoot(chainedCallPath);
333
+ continue;
334
+ }
335
+ accessedTopLevelFieldNames.add(fieldName);
283
336
  if (memberExprPath) analyzeOpaqueUsage(memberExprPath, [fieldName]);
284
- } else {
285
- hasUntrackedReferenceAccess = true;
286
- break;
287
- }
288
- } else if (callParent && babelTypes.isVariableDeclarator(callParent) && babelTypes.isObjectPattern(callParent.id) && callExprPath && collectFieldsFromObjectPattern(callParent.id, callExprPath, accessedTopLevelFieldNames)) {} else {
289
- hasUntrackedReferenceAccess = true;
290
- break;
337
+ } else return true;
338
+ } else if (callParent && babelTypes.isVariableDeclarator(callParent) && babelTypes.isObjectPattern(callParent.id) && callExprPath && collectFieldsFromObjectPattern(callParent.id, callExprPath, accessedTopLevelFieldNames)) {} else return true;
339
+ } else return true;
340
+ }
341
+ return false;
342
+ };
343
+ /**
344
+ * Analyses how the content root referenced by `rootPath` is consumed.
345
+ *
346
+ * `rootPath` is the `useIntlayer('key')` call itself, or any expression that
347
+ * evaluates back to the same content object — currently the result of a
348
+ * chainable runtime helper such as `.onChange(…)`.
349
+ */
350
+ const analyzeContentRoot = (rootPath) => {
351
+ const parentNode = rootPath.parent;
352
+ if (babelTypes.isVariableDeclarator(parentNode) && babelTypes.isObjectPattern(parentNode.id)) {
353
+ const accessedFieldNames = /* @__PURE__ */ new Set();
354
+ if (collectFieldsFromObjectPattern(parentNode.id, rootPath, accessedFieldNames)) recordFieldUsage(pruneContext, dictionaryKey, accessedFieldNames);
355
+ else recordFieldUsage(pruneContext, dictionaryKey, "all");
356
+ return;
357
+ }
358
+ if ((babelTypes.isMemberExpression(parentNode) || babelTypes.isOptionalMemberExpression(parentNode)) && parentNode.object === rootPath.node) {
359
+ let fieldName;
360
+ if (!parentNode.computed && babelTypes.isIdentifier(parentNode.property)) fieldName = parentNode.property.name;
361
+ else if (parentNode.computed && babelTypes.isStringLiteral(parentNode.property)) fieldName = parentNode.property.value;
362
+ if (fieldName) {
363
+ const memberExprPath = rootPath.parentPath;
364
+ const chainedCallPath = getChainedRuntimeCallPath(memberExprPath, fieldName);
365
+ if (chainedCallPath) {
366
+ analyzeChainedCallbackArguments(chainedCallPath);
367
+ analyzeContentRoot(chainedCallPath);
368
+ return;
291
369
  }
292
- } else {
293
- hasUntrackedReferenceAccess = true;
294
- break;
370
+ recordFieldUsage(pruneContext, dictionaryKey, /* @__PURE__ */ new Set([fieldName]));
371
+ if (memberExprPath) analyzeOpaqueUsage(memberExprPath, [fieldName]);
372
+ } else markUntrackedBinding();
373
+ return;
374
+ }
375
+ if (babelTypes.isVariableDeclarator(parentNode) && babelTypes.isIdentifier(parentNode.id)) {
376
+ const variableName = parentNode.id.name;
377
+ const variableBinding = rootPath.scope.getBinding(variableName);
378
+ if (!variableBinding) {
379
+ markUntrackedBinding();
380
+ return;
295
381
  }
382
+ const accessedTopLevelFieldNames = /* @__PURE__ */ new Set();
383
+ if (analyzeContentBindingReferences(variableBinding, accessedTopLevelFieldNames)) markUntrackedBinding();
384
+ else if (isSfcFile) deferFrameworkAnalysis(variableName);
385
+ else if (variableBinding.referencePaths.length === 0) markUntrackedBinding();
386
+ else recordFieldUsage(pruneContext, dictionaryKey, accessedTopLevelFieldNames);
387
+ return;
296
388
  }
297
- if (hasUntrackedReferenceAccess) markUntrackedBinding();
298
- else if (isSfcFile) deferFrameworkAnalysis(variableName);
299
- else if (variableBinding.referencePaths.length === 0) markUntrackedBinding();
300
- else recordFieldUsage(pruneContext, dictionaryKey, accessedTopLevelFieldNames);
301
- return;
302
- }
303
- if (babelTypes.isExpressionStatement(parentNode)) return;
304
- markUntrackedBinding();
389
+ if (babelTypes.isExpressionStatement(parentNode)) return;
390
+ markUntrackedBinding();
391
+ };
392
+ analyzeContentRoot(callExpressionPath);
305
393
  };
306
394
  /**
307
395
  * Reads a fully-static string from an AST node. Returns `undefined` for
@@ -645,5 +733,5 @@ const makeUsageAnalyzerBabelPlugin = (pruneContext, options) => ({ types: babelT
645
733
  };
646
734
 
647
735
  //#endregion
648
- export { DEFAULT_COMPAT_CALLERS, INTLAYER_CALLER_NAMES, createPruneContext, makeUsageAnalyzerBabelPlugin, preserveNestedDictionaryFields };
736
+ export { CHAINABLE_RUNTIME_METHOD_NAMES, DEFAULT_COMPAT_CALLERS, INTLAYER_CALLER_NAMES, createPruneContext, makeUsageAnalyzerBabelPlugin, preserveNestedDictionaryFields };
649
737
  //# sourceMappingURL=babel-plugin-intlayer-usage-analyzer.mjs.map