@intlayer/babel 8.6.5 → 8.6.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -48,7 +48,33 @@ const handleJsxInsertionBabel = (path, fileCode, existingKeys, getComponentKeyFo
48
48
  originalExpr: code
49
49
  });
50
50
  hasVariables = true;
51
- } else return false;
51
+ } else if (t.isTemplateLiteral(expr)) for (let i = 0; i < expr.quasis.length; i++) {
52
+ parts.push({
53
+ type: "text",
54
+ value: expr.quasis[i].value.raw
55
+ });
56
+ if (i < expr.expressions.length) {
57
+ const subExpr = expr.expressions[i];
58
+ if (t.isIdentifier(subExpr)) {
59
+ parts.push({
60
+ type: "var",
61
+ value: subExpr.name,
62
+ originalExpr: subExpr.name
63
+ });
64
+ hasVariables = true;
65
+ } else if (t.isMemberExpression(subExpr)) {
66
+ const code = fileCode.substring(subExpr.start, subExpr.end);
67
+ const varName = t.isIdentifier(subExpr.property) ? subExpr.property.name : "var";
68
+ parts.push({
69
+ type: "var",
70
+ value: varName,
71
+ originalExpr: code
72
+ });
73
+ hasVariables = true;
74
+ } else return false;
75
+ }
76
+ }
77
+ else return false;
52
78
  }
53
79
  else return false;
54
80
  if (!hasSignificantText) return false;
@@ -97,6 +123,9 @@ const extractBabelContentForComponents = (ast, fileCode, existingKeys, defaultKe
97
123
  let globalFileKey;
98
124
  const componentPaths = [];
99
125
  traverse(ast, {
126
+ Program(path) {
127
+ componentPaths.push(path);
128
+ },
100
129
  FunctionDeclaration(path) {
101
130
  componentPaths.push(path);
102
131
  },
@@ -113,6 +142,13 @@ const extractBabelContentForComponents = (ast, fileCode, existingKeys, defaultKe
113
142
  componentKeyMap.set(path.node, existingInfo.key);
114
143
  usedKeysInFile.add(existingInfo.key);
115
144
  hookMap.set(path.node, existingInfo.hook);
145
+ } else if (path.isProgram()) {
146
+ if (!globalFileKey) {
147
+ globalFileKey = resolveDictionaryKey(defaultKey, filePath, configuration, unmergedDictionaries, usedKeysInFile);
148
+ usedKeysInFile.add(globalFileKey);
149
+ }
150
+ componentKeyMap.set(path.node, globalFileKey);
151
+ hookMap.set(path.node, "getIntlayer");
116
152
  } else {
117
153
  if (!globalFileKey) {
118
154
  globalFileKey = resolveDictionaryKey(defaultKey, filePath, configuration, unmergedDictionaries, usedKeysInFile);
@@ -121,7 +157,8 @@ const extractBabelContentForComponents = (ast, fileCode, existingKeys, defaultKe
121
157
  componentKeyMap.set(path.node, globalFileKey);
122
158
  const compName = getComponentName(path);
123
159
  const isComponent = compName ? /^[A-Z]/.test(compName) : false;
124
- hookMap.set(path.node, isComponent ? "useIntlayer" : "getIntlayer");
160
+ const isHook = compName ? /^use[A-Z]/.test(compName) : false;
161
+ hookMap.set(path.node, isComponent || isHook ? "useIntlayer" : "getIntlayer");
125
162
  }
126
163
  }
127
164
  const getComponentKeyForPath = (path) => {
@@ -191,42 +228,93 @@ const extractBabelContentForComponents = (ast, fileCode, existingKeys, defaultKe
191
228
  type: "string-literal",
192
229
  componentKey
193
230
  });
231
+ },
232
+ TemplateLiteral(path) {
233
+ if (handledNodes.has(path.node)) return;
234
+ const { quasis, expressions } = path.node;
235
+ let combinedString = "";
236
+ const variables = [];
237
+ let hasSignificantText = false;
238
+ for (let i = 0; i < quasis.length; i++) {
239
+ const text = quasis[i].value.raw;
240
+ combinedString += text;
241
+ if (text.trim().length > 0) hasSignificantText = true;
242
+ if (i < expressions.length) {
243
+ const expr = expressions[i];
244
+ if (t.isIdentifier(expr)) {
245
+ combinedString += `{{${expr.name}}}`;
246
+ variables.push(`${expr.name}: ${expr.name}`);
247
+ } else if (t.isMemberExpression(expr)) {
248
+ const code = fileCode.substring(expr.start, expr.end);
249
+ const varName = t.isIdentifier(expr.property) ? expr.property.name : "var";
250
+ combinedString += `{{${varName}}}`;
251
+ variables.push(`${varName}: ${code}`);
252
+ } else return;
253
+ }
254
+ }
255
+ if (!hasSignificantText) return;
256
+ const cleanString = combinedString.replace(/\s+/g, " ").trim();
257
+ if (!shouldExtract(cleanString)) return;
258
+ const componentKey = getComponentKeyForPath(path);
259
+ const key = getOrGenerateKey(cleanString, componentKey, existingKeys, extractedContent);
260
+ const uniqueVars = Array.from(new Set(variables));
261
+ replacements.push({
262
+ path,
263
+ key,
264
+ type: "template-literal",
265
+ componentKey,
266
+ variables: uniqueVars
267
+ });
194
268
  }
195
269
  });
196
270
  const componentsNeedingHooks = /* @__PURE__ */ new Set();
197
- for (const componentPath of componentPaths) if (replacements.some((replacement) => {
198
- let current = replacement.path;
199
- while (current) {
200
- if (current.node === componentPath.node) return true;
201
- current = current.parentPath;
271
+ for (const componentPath of componentPaths) {
272
+ if (componentPath.isProgram()) {
273
+ if (replacements.some((replacement) => {
274
+ let current = replacement.path;
275
+ while (current) {
276
+ if (current.node === componentPath.node) return true;
277
+ if (componentPaths.some((p) => p !== componentPath && p.node === current?.node)) return false;
278
+ current = current.parentPath;
279
+ }
280
+ return false;
281
+ })) componentsNeedingHooks.add(componentPath);
282
+ continue;
202
283
  }
203
- return false;
204
- })) {
205
- const key = componentKeyMap.get(componentPath.node);
206
- let ancestorProvidesKey = false;
207
- let currentPath = componentPath.parentPath;
208
- while (currentPath) {
209
- const ancestorPath = componentPaths.find((path) => path.node === currentPath?.node);
210
- if (ancestorPath) {
211
- if (componentKeyMap.get(ancestorPath.node) === key) {
212
- const ancestorHasReplacements = replacements.some((replacement) => {
213
- let rPath = replacement.path;
214
- while (rPath) {
215
- if (rPath.node === ancestorPath.node) return true;
216
- rPath = rPath.parentPath;
284
+ if (replacements.some((replacement) => {
285
+ let current = replacement.path;
286
+ while (current) {
287
+ if (current.node === componentPath.node) return true;
288
+ current = current.parentPath;
289
+ }
290
+ return false;
291
+ })) {
292
+ const key = componentKeyMap.get(componentPath.node);
293
+ let ancestorProvidesKey = false;
294
+ let currentPath = componentPath.parentPath;
295
+ while (currentPath) {
296
+ const ancestorPath = componentPaths.find((path) => path.node === currentPath?.node);
297
+ if (ancestorPath && !ancestorPath.isProgram()) {
298
+ if (componentKeyMap.get(ancestorPath.node) === key) {
299
+ const ancestorHasReplacements = replacements.some((replacement) => {
300
+ let rPath = replacement.path;
301
+ while (rPath) {
302
+ if (rPath.node === ancestorPath.node) return true;
303
+ rPath = rPath.parentPath;
304
+ }
305
+ return false;
306
+ });
307
+ const existingInfo = getExistingIntlayerInfo(ancestorPath);
308
+ if (ancestorHasReplacements || existingInfo) {
309
+ ancestorProvidesKey = true;
310
+ break;
217
311
  }
218
- return false;
219
- });
220
- const existingInfo = getExistingIntlayerInfo(ancestorPath);
221
- if (ancestorHasReplacements || existingInfo) {
222
- ancestorProvidesKey = true;
223
- break;
224
312
  }
225
313
  }
314
+ currentPath = currentPath.parentPath;
226
315
  }
227
- currentPath = currentPath.parentPath;
316
+ if (!ancestorProvidesKey) componentsNeedingHooks.add(componentPath);
228
317
  }
229
- if (!ancestorProvidesKey) componentsNeedingHooks.add(componentPath);
230
318
  }
231
319
  return {
232
320
  extractedContent,
@@ -1 +1 @@
1
- {"version":3,"file":"babelProcessor.mjs","names":[],"sources":["../../../src/extractContent/babelProcessor.ts"],"sourcesContent":["import _traverse, { type NodePath } from '@babel/traverse';\nimport * as t from '@babel/types';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { resolveDictionaryKey } from '../extractContent/utils';\nimport {\n ATTRIBUTES_TO_EXTRACT,\n getComponentName,\n getExistingIntlayerInfo,\n getOrGenerateKey,\n shouldExtract,\n} from './utils';\n\nexport type BabelReplacement = {\n path: NodePath;\n key: string;\n type:\n | 'jsx-text'\n | 'jsx-attribute'\n | 'string-literal'\n | 'jsx-insertion'\n | 'jsx-text-combined';\n componentKey: string;\n childrenToReplace?: t.Node[];\n variables?: string[];\n};\n\n// CJS/ESM interop: @babel/traverse exports its function as `.default` in CJS bundles\nconst traverse = (\n typeof _traverse === 'function' ? _traverse : (_traverse as any).default\n) as typeof _traverse;\n\n/**\n * Handles JSX insertions (elements with multiple children, including expressions).\n * Replaces complex JSX structures with variable-based translations.\n */\nexport const handleJsxInsertionBabel = (\n path: NodePath<t.JSXElement | t.JSXFragment>,\n fileCode: string,\n existingKeys: Set<string>,\n getComponentKeyForPath: (path: NodePath) => string,\n extractedContent: Record<string, Record<string, string>>,\n replacements: BabelReplacement[],\n handledNodes: Set<t.Node>\n): boolean => {\n const children = path.node.children;\n\n if (children.length <= 1) return false;\n\n const parts: {\n type: 'text' | 'var';\n value: string;\n originalExpr?: string;\n }[] = [];\n let hasSignificantText = false;\n let hasVariables = false;\n\n for (const child of children) {\n if (t.isJSXText(child)) {\n const text = child.value;\n\n if (text.trim().length > 0) hasSignificantText = true;\n\n parts.push({ type: 'text', value: text });\n } else if (t.isJSXExpressionContainer(child)) {\n if (t.isJSXEmptyExpression(child.expression)) {\n parts.push({ type: 'text', value: '' });\n } else {\n const expr = child.expression;\n\n if (t.isIdentifier(expr)) {\n parts.push({\n type: 'var',\n value: expr.name,\n originalExpr: expr.name,\n });\n hasVariables = true;\n } else if (t.isMemberExpression(expr)) {\n const code = fileCode.substring(expr.start!, expr.end!);\n\n const varName = t.isIdentifier(expr.property)\n ? expr.property.name\n : 'var';\n\n parts.push({ type: 'var', value: varName, originalExpr: code });\n\n hasVariables = true;\n } else {\n return false;\n }\n }\n } else {\n return false;\n }\n }\n\n if (!hasSignificantText) return false;\n\n let combinedString = '';\n for (const part of parts) {\n if (part.type === 'var') combinedString += `{{${part.value}}}`;\n else combinedString += part.value;\n }\n\n const cleanString = combinedString.replace(/\\s+/g, ' ').trim();\n\n if (shouldExtract(cleanString)) {\n const componentKey = getComponentKeyForPath(path);\n const key = getOrGenerateKey(\n cleanString,\n componentKey,\n existingKeys,\n extractedContent\n );\n\n const varMap = parts\n .filter((part) => part.type === 'var')\n .map((part) => `${part.value}: ${part.originalExpr}`);\n const uniqueVars = Array.from(new Set(varMap));\n\n if (hasVariables) {\n replacements.push({\n path,\n key,\n type: 'jsx-insertion',\n componentKey,\n childrenToReplace: children,\n variables: uniqueVars,\n });\n } else {\n replacements.push({\n path,\n key,\n type: 'jsx-text-combined',\n componentKey,\n childrenToReplace: children,\n });\n }\n\n children.forEach((child) => {\n handledNodes.add(child);\n });\n return true;\n }\n\n return false;\n};\n\n/**\n * Traverses the AST to identify components and extract content.\n * Returns extraction results and metadata about which components need Intlayer hooks.\n */\nexport const extractBabelContentForComponents = (\n ast: t.File,\n fileCode: string,\n existingKeys: Set<string>,\n defaultKey: string = 'default',\n configuration: IntlayerConfig,\n filePath: string,\n unmergedDictionaries: Record<string, unknown> = {}\n): {\n extractedContent: Record<string, Record<string, string>>;\n replacements: BabelReplacement[];\n componentsNeedingHooks: Set<NodePath>;\n componentKeyMap: Map<t.Node, string>;\n componentPaths: NodePath[];\n hookMap: Map<t.Node, 'useIntlayer' | 'getIntlayer'>;\n isSolid: boolean;\n} => {\n const extractedContent: Record<string, Record<string, string>> = {};\n const replacements: BabelReplacement[] = [];\n const handledNodes = new Set<t.Node>();\n const componentKeyMap = new Map<t.Node, string>();\n const hookMap = new Map<t.Node, 'useIntlayer' | 'getIntlayer'>();\n const usedKeysInFile = new Set<string>();\n let globalFileKey: string | undefined;\n\n const componentPaths: NodePath[] = [];\n\n traverse(ast, {\n FunctionDeclaration(path) {\n componentPaths.push(path);\n },\n ArrowFunctionExpression(path) {\n componentPaths.push(path);\n },\n FunctionExpression(path) {\n componentPaths.push(path);\n },\n });\n\n for (const path of componentPaths) {\n const existingInfo = getExistingIntlayerInfo(path);\n\n if (existingInfo) {\n componentKeyMap.set(path.node, existingInfo.key);\n usedKeysInFile.add(existingInfo.key);\n hookMap.set(path.node, existingInfo.hook);\n } else {\n if (!globalFileKey) {\n globalFileKey = resolveDictionaryKey(\n defaultKey,\n filePath,\n configuration,\n unmergedDictionaries,\n usedKeysInFile\n );\n usedKeysInFile.add(globalFileKey);\n }\n componentKeyMap.set(path.node, globalFileKey);\n\n const compName = getComponentName(path);\n const isComponent = compName ? /^[A-Z]/.test(compName) : false;\n hookMap.set(path.node, isComponent ? 'useIntlayer' : 'getIntlayer');\n }\n }\n\n const getComponentKeyForPath = (path: NodePath): string => {\n let current: NodePath | null = path;\n while (current) {\n if (componentKeyMap.has(current.node)) {\n return componentKeyMap.get(current.node)!;\n }\n current = current.parentPath;\n }\n return globalFileKey || defaultKey;\n };\n\n traverse(ast, {\n JSXElement(path) {\n if (handledNodes.has(path.node)) return;\n\n handleJsxInsertionBabel(\n path,\n fileCode,\n existingKeys,\n getComponentKeyForPath,\n extractedContent,\n replacements,\n handledNodes\n );\n },\n JSXFragment(path) {\n if (handledNodes.has(path.node)) return;\n\n handleJsxInsertionBabel(\n path,\n fileCode,\n existingKeys,\n getComponentKeyForPath,\n extractedContent,\n replacements,\n handledNodes\n );\n },\n JSXText(path) {\n if (handledNodes.has(path.node)) return;\n\n const text = path.node.value;\n\n if (shouldExtract(text)) {\n const componentKey = getComponentKeyForPath(path);\n const key = getOrGenerateKey(\n text.replace(/\\s+/g, ' ').trim(),\n componentKey,\n existingKeys,\n extractedContent\n );\n replacements.push({ path, key, type: 'jsx-text', componentKey });\n }\n },\n JSXAttribute(path) {\n if (handledNodes.has(path.node)) return;\n\n const name = path.node.name.name;\n\n if (\n typeof name !== 'string' ||\n !ATTRIBUTES_TO_EXTRACT.includes(name as any)\n )\n return;\n const value = path.node.value;\n\n if (t.isStringLiteral(value) && shouldExtract(value.value)) {\n const componentKey = getComponentKeyForPath(path);\n const key = getOrGenerateKey(\n value.value.trim(),\n componentKey,\n existingKeys,\n extractedContent\n );\n replacements.push({ path, key, type: 'jsx-attribute', componentKey });\n }\n },\n StringLiteral(path) {\n if (handledNodes.has(path.node)) return;\n\n const text = path.node.value;\n\n if (!shouldExtract(text)) return;\n\n const parent = path.parentPath;\n\n if (\n parent.isImportDeclaration() ||\n parent.isImportSpecifier() ||\n parent.isExportDeclaration()\n )\n return;\n\n if (parent.isJSXAttribute()) return;\n\n if (\n parent.isCallExpression() &&\n t.isMemberExpression(parent.node.callee)\n ) {\n if (\n t.isIdentifier(parent.node.callee.object) &&\n parent.node.callee.object.name === 'console' &&\n t.isIdentifier(parent.node.callee.property) &&\n parent.node.callee.property.name === 'log'\n ) {\n return;\n }\n }\n\n if (parent.isObjectProperty() && parent.node.key === path.node) return;\n\n if (parent.isMemberExpression() && parent.node.property === path.node)\n return;\n\n const componentKey = getComponentKeyForPath(path);\n const key = getOrGenerateKey(\n text.trim(),\n componentKey,\n existingKeys,\n extractedContent\n );\n replacements.push({ path, key, type: 'string-literal', componentKey });\n },\n });\n\n const componentsNeedingHooks = new Set<NodePath>();\n for (const componentPath of componentPaths) {\n const hasReplacements = replacements.some((replacement) => {\n let current: NodePath | null = replacement.path;\n while (current) {\n if (current.node === componentPath.node) return true;\n\n current = current.parentPath;\n }\n return false;\n });\n\n if (hasReplacements) {\n const key = componentKeyMap.get(componentPath.node)!;\n let ancestorProvidesKey = false;\n let currentPath: NodePath | null = componentPath.parentPath;\n while (currentPath) {\n const ancestorPath = componentPaths.find(\n (path) => path.node === currentPath?.node\n );\n\n if (ancestorPath) {\n const ancestorKey = componentKeyMap.get(ancestorPath.node);\n\n if (ancestorKey === key) {\n const ancestorHasReplacements = replacements.some((replacement) => {\n let rPath: NodePath | null = replacement.path;\n while (rPath) {\n if (rPath.node === ancestorPath.node) return true;\n\n rPath = rPath.parentPath;\n }\n return false;\n });\n const existingInfo = getExistingIntlayerInfo(ancestorPath);\n\n if (ancestorHasReplacements || existingInfo) {\n ancestorProvidesKey = true;\n break;\n }\n }\n }\n currentPath = currentPath.parentPath;\n }\n\n if (!ancestorProvidesKey) {\n componentsNeedingHooks.add(componentPath);\n }\n }\n }\n\n return {\n extractedContent,\n replacements,\n componentsNeedingHooks,\n componentKeyMap,\n componentPaths,\n hookMap,\n isSolid: false,\n };\n};\n\n/**\n * High-level function to extract content from TS/JS/JSX/TSX AST.\n */\nexport const extractTsContent = (\n ast: t.File,\n fileCode: string,\n existingKeys: Set<string>,\n configuration: IntlayerConfig,\n filePath: string,\n unmergedDictionaries: Record<string, unknown> = {}\n): {\n extractedContent: Record<string, string>;\n replacements: BabelReplacement[];\n} => {\n const { extractedContent, replacements } = extractBabelContentForComponents(\n ast,\n fileCode,\n existingKeys,\n 'default',\n configuration,\n filePath,\n unmergedDictionaries\n );\n\n const flatContent: Record<string, string> = {};\n for (const group of Object.values(extractedContent)) {\n Object.assign(flatContent, group);\n }\n\n return { extractedContent: flatContent, replacements };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"babelProcessor.mjs","names":[],"sources":["../../../src/extractContent/babelProcessor.ts"],"sourcesContent":["import _traverse, { type NodePath } from '@babel/traverse';\nimport * as t from '@babel/types';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { resolveDictionaryKey } from '../extractContent/utils';\nimport {\n ATTRIBUTES_TO_EXTRACT,\n getComponentName,\n getExistingIntlayerInfo,\n getOrGenerateKey,\n shouldExtract,\n} from './utils';\n\nexport type BabelReplacement = {\n path: NodePath;\n key: string;\n type:\n | 'jsx-text'\n | 'jsx-attribute'\n | 'string-literal'\n | 'jsx-insertion'\n | 'jsx-text-combined'\n | 'template-literal';\n componentKey: string;\n childrenToReplace?: t.Node[];\n variables?: string[];\n};\n\n// CJS/ESM interop: @babel/traverse exports its function as `.default` in CJS bundles\nconst traverse = (\n typeof _traverse === 'function' ? _traverse : (_traverse as any).default\n) as typeof _traverse;\n\n/**\n * Handles JSX insertions (elements with multiple children, including expressions).\n * Replaces complex JSX structures with variable-based translations.\n */\nexport const handleJsxInsertionBabel = (\n path: NodePath<t.JSXElement | t.JSXFragment>,\n fileCode: string,\n existingKeys: Set<string>,\n getComponentKeyForPath: (path: NodePath) => string,\n extractedContent: Record<string, Record<string, string>>,\n replacements: BabelReplacement[],\n handledNodes: Set<t.Node>\n): boolean => {\n const children = path.node.children;\n\n if (children.length <= 1) return false;\n\n const parts: {\n type: 'text' | 'var';\n value: string;\n originalExpr?: string;\n }[] = [];\n let hasSignificantText = false;\n let hasVariables = false;\n\n for (const child of children) {\n if (t.isJSXText(child)) {\n const text = child.value;\n\n if (text.trim().length > 0) hasSignificantText = true;\n\n parts.push({ type: 'text', value: text });\n } else if (t.isJSXExpressionContainer(child)) {\n if (t.isJSXEmptyExpression(child.expression)) {\n parts.push({ type: 'text', value: '' });\n } else {\n const expr = child.expression;\n\n if (t.isIdentifier(expr)) {\n parts.push({\n type: 'var',\n value: expr.name,\n originalExpr: expr.name,\n });\n hasVariables = true;\n } else if (t.isMemberExpression(expr)) {\n const code = fileCode.substring(expr.start!, expr.end!);\n\n const varName = t.isIdentifier(expr.property)\n ? expr.property.name\n : 'var';\n\n parts.push({ type: 'var', value: varName, originalExpr: code });\n\n hasVariables = true;\n } else if (t.isTemplateLiteral(expr)) {\n for (let i = 0; i < expr.quasis.length; i++) {\n parts.push({ type: 'text', value: expr.quasis[i].value.raw });\n if (i < expr.expressions.length) {\n const subExpr = expr.expressions[i];\n if (t.isIdentifier(subExpr)) {\n parts.push({\n type: 'var',\n value: subExpr.name,\n originalExpr: subExpr.name,\n });\n hasVariables = true;\n } else if (t.isMemberExpression(subExpr)) {\n const code = fileCode.substring(subExpr.start!, subExpr.end!);\n const varName = t.isIdentifier(subExpr.property)\n ? subExpr.property.name\n : 'var';\n parts.push({ type: 'var', value: varName, originalExpr: code });\n hasVariables = true;\n } else {\n return false;\n }\n }\n }\n } else {\n return false;\n }\n }\n } else {\n return false;\n }\n }\n\n if (!hasSignificantText) return false;\n\n let combinedString = '';\n for (const part of parts) {\n if (part.type === 'var') combinedString += `{{${part.value}}}`;\n else combinedString += part.value;\n }\n\n const cleanString = combinedString.replace(/\\s+/g, ' ').trim();\n\n if (shouldExtract(cleanString)) {\n const componentKey = getComponentKeyForPath(path);\n const key = getOrGenerateKey(\n cleanString,\n componentKey,\n existingKeys,\n extractedContent\n );\n\n const varMap = parts\n .filter((part) => part.type === 'var')\n .map((part) => `${part.value}: ${part.originalExpr}`);\n const uniqueVars = Array.from(new Set(varMap));\n\n if (hasVariables) {\n replacements.push({\n path,\n key,\n type: 'jsx-insertion',\n componentKey,\n childrenToReplace: children,\n variables: uniqueVars,\n });\n } else {\n replacements.push({\n path,\n key,\n type: 'jsx-text-combined',\n componentKey,\n childrenToReplace: children,\n });\n }\n\n children.forEach((child) => {\n handledNodes.add(child);\n });\n return true;\n }\n\n return false;\n};\n\n/**\n * Traverses the AST to identify components and extract content.\n * Returns extraction results and metadata about which components need Intlayer hooks.\n */\nexport const extractBabelContentForComponents = (\n ast: t.File,\n fileCode: string,\n existingKeys: Set<string>,\n defaultKey: string = 'default',\n configuration: IntlayerConfig,\n filePath: string,\n unmergedDictionaries: Record<string, unknown> = {}\n): {\n extractedContent: Record<string, Record<string, string>>;\n replacements: BabelReplacement[];\n componentsNeedingHooks: Set<NodePath>;\n componentKeyMap: Map<t.Node, string>;\n componentPaths: NodePath[];\n hookMap: Map<t.Node, 'useIntlayer' | 'getIntlayer'>;\n isSolid: boolean;\n} => {\n const extractedContent: Record<string, Record<string, string>> = {};\n const replacements: BabelReplacement[] = [];\n const handledNodes = new Set<t.Node>();\n const componentKeyMap = new Map<t.Node, string>();\n const hookMap = new Map<t.Node, 'useIntlayer' | 'getIntlayer'>();\n const usedKeysInFile = new Set<string>();\n let globalFileKey: string | undefined;\n\n const componentPaths: NodePath[] = [];\n\n traverse(ast, {\n Program(path) {\n componentPaths.push(path);\n },\n FunctionDeclaration(path) {\n componentPaths.push(path);\n },\n ArrowFunctionExpression(path) {\n componentPaths.push(path);\n },\n FunctionExpression(path) {\n componentPaths.push(path);\n },\n });\n\n for (const path of componentPaths) {\n const existingInfo = getExistingIntlayerInfo(path);\n\n if (existingInfo) {\n componentKeyMap.set(path.node, existingInfo.key);\n usedKeysInFile.add(existingInfo.key);\n hookMap.set(path.node, existingInfo.hook);\n } else {\n if (path.isProgram()) {\n if (!globalFileKey) {\n globalFileKey = resolveDictionaryKey(\n defaultKey,\n filePath,\n configuration,\n unmergedDictionaries,\n usedKeysInFile\n );\n usedKeysInFile.add(globalFileKey);\n }\n componentKeyMap.set(path.node, globalFileKey);\n hookMap.set(path.node, 'getIntlayer');\n } else {\n if (!globalFileKey) {\n globalFileKey = resolveDictionaryKey(\n defaultKey,\n filePath,\n configuration,\n unmergedDictionaries,\n usedKeysInFile\n );\n usedKeysInFile.add(globalFileKey);\n }\n componentKeyMap.set(path.node, globalFileKey);\n\n const compName = getComponentName(path);\n const isComponent = compName ? /^[A-Z]/.test(compName) : false;\n const isHook = compName ? /^use[A-Z]/.test(compName) : false;\n hookMap.set(\n path.node,\n isComponent || isHook ? 'useIntlayer' : 'getIntlayer'\n );\n }\n }\n }\n\n const getComponentKeyForPath = (path: NodePath): string => {\n let current: NodePath | null = path;\n while (current) {\n if (componentKeyMap.has(current.node)) {\n return componentKeyMap.get(current.node)!;\n }\n current = current.parentPath;\n }\n return globalFileKey || defaultKey;\n };\n\n traverse(ast, {\n JSXElement(path) {\n if (handledNodes.has(path.node)) return;\n\n handleJsxInsertionBabel(\n path,\n fileCode,\n existingKeys,\n getComponentKeyForPath,\n extractedContent,\n replacements,\n handledNodes\n );\n },\n JSXFragment(path) {\n if (handledNodes.has(path.node)) return;\n\n handleJsxInsertionBabel(\n path,\n fileCode,\n existingKeys,\n getComponentKeyForPath,\n extractedContent,\n replacements,\n handledNodes\n );\n },\n JSXText(path) {\n if (handledNodes.has(path.node)) return;\n\n const text = path.node.value;\n\n if (shouldExtract(text)) {\n const componentKey = getComponentKeyForPath(path);\n const key = getOrGenerateKey(\n text.replace(/\\s+/g, ' ').trim(),\n componentKey,\n existingKeys,\n extractedContent\n );\n replacements.push({ path, key, type: 'jsx-text', componentKey });\n }\n },\n JSXAttribute(path) {\n if (handledNodes.has(path.node)) return;\n\n const name = path.node.name.name;\n\n if (\n typeof name !== 'string' ||\n !ATTRIBUTES_TO_EXTRACT.includes(name as any)\n )\n return;\n const value = path.node.value;\n\n if (t.isStringLiteral(value) && shouldExtract(value.value)) {\n const componentKey = getComponentKeyForPath(path);\n const key = getOrGenerateKey(\n value.value.trim(),\n componentKey,\n existingKeys,\n extractedContent\n );\n replacements.push({ path, key, type: 'jsx-attribute', componentKey });\n }\n },\n StringLiteral(path) {\n if (handledNodes.has(path.node)) return;\n\n const text = path.node.value;\n\n if (!shouldExtract(text)) return;\n\n const parent = path.parentPath;\n\n if (\n parent.isImportDeclaration() ||\n parent.isImportSpecifier() ||\n parent.isExportDeclaration()\n )\n return;\n\n if (parent.isJSXAttribute()) return;\n\n if (\n parent.isCallExpression() &&\n t.isMemberExpression(parent.node.callee)\n ) {\n if (\n t.isIdentifier(parent.node.callee.object) &&\n parent.node.callee.object.name === 'console' &&\n t.isIdentifier(parent.node.callee.property) &&\n parent.node.callee.property.name === 'log'\n ) {\n return;\n }\n }\n\n if (parent.isObjectProperty() && parent.node.key === path.node) return;\n\n if (parent.isMemberExpression() && parent.node.property === path.node)\n return;\n\n const componentKey = getComponentKeyForPath(path);\n const key = getOrGenerateKey(\n text.trim(),\n componentKey,\n existingKeys,\n extractedContent\n );\n replacements.push({ path, key, type: 'string-literal', componentKey });\n },\n TemplateLiteral(path) {\n if (handledNodes.has(path.node)) return;\n\n const { quasis, expressions } = path.node;\n\n // Build the combined string with placeholders\n let combinedString = '';\n const variables: string[] = [];\n let hasSignificantText = false;\n\n for (let i = 0; i < quasis.length; i++) {\n const text = quasis[i].value.raw;\n combinedString += text;\n if (text.trim().length > 0) hasSignificantText = true;\n\n if (i < expressions.length) {\n const expr = expressions[i];\n if (t.isIdentifier(expr)) {\n combinedString += `{{${expr.name}}}`;\n variables.push(`${expr.name}: ${expr.name}`);\n } else if (t.isMemberExpression(expr)) {\n const code = fileCode.substring(expr.start!, expr.end!);\n const varName = t.isIdentifier(expr.property)\n ? expr.property.name\n : 'var';\n combinedString += `{{${varName}}}`;\n variables.push(`${varName}: ${code}`);\n } else {\n // Complex expression in template literal, skip\n return;\n }\n }\n }\n\n if (!hasSignificantText) return;\n\n const cleanString = combinedString.replace(/\\s+/g, ' ').trim();\n\n if (!shouldExtract(cleanString)) return;\n\n const componentKey = getComponentKeyForPath(path);\n const key = getOrGenerateKey(\n cleanString,\n componentKey,\n existingKeys,\n extractedContent\n );\n\n const uniqueVars = Array.from(new Set(variables));\n\n replacements.push({\n path,\n key,\n type: 'template-literal',\n componentKey,\n variables: uniqueVars,\n });\n },\n });\n\n const componentsNeedingHooks = new Set<NodePath>();\n for (const componentPath of componentPaths) {\n if (componentPath.isProgram()) {\n const hasDirectReplacements = replacements.some((replacement) => {\n let current: NodePath | null = replacement.path;\n while (current) {\n if (current.node === componentPath.node) {\n return true;\n }\n const isOtherComponent = componentPaths.some(\n (p) => p !== componentPath && p.node === current?.node\n );\n if (isOtherComponent) {\n return false;\n }\n current = current.parentPath;\n }\n return false;\n });\n\n if (hasDirectReplacements) {\n componentsNeedingHooks.add(componentPath);\n }\n continue;\n }\n\n const hasReplacements = replacements.some((replacement) => {\n let current: NodePath | null = replacement.path;\n while (current) {\n if (current.node === componentPath.node) return true;\n\n current = current.parentPath;\n }\n return false;\n });\n\n if (hasReplacements) {\n const key = componentKeyMap.get(componentPath.node)!;\n let ancestorProvidesKey = false;\n let currentPath: NodePath | null = componentPath.parentPath;\n while (currentPath) {\n const ancestorPath = componentPaths.find(\n (path) => path.node === currentPath?.node\n );\n\n if (ancestorPath && !ancestorPath.isProgram()) {\n const ancestorKey = componentKeyMap.get(ancestorPath.node);\n\n if (ancestorKey === key) {\n const ancestorHasReplacements = replacements.some((replacement) => {\n let rPath: NodePath | null = replacement.path;\n while (rPath) {\n if (rPath.node === ancestorPath.node) return true;\n\n rPath = rPath.parentPath;\n }\n return false;\n });\n const existingInfo = getExistingIntlayerInfo(ancestorPath);\n\n if (ancestorHasReplacements || existingInfo) {\n ancestorProvidesKey = true;\n break;\n }\n }\n }\n currentPath = currentPath.parentPath;\n }\n\n if (!ancestorProvidesKey) {\n componentsNeedingHooks.add(componentPath);\n }\n }\n }\n\n return {\n extractedContent,\n replacements,\n componentsNeedingHooks,\n componentKeyMap,\n componentPaths,\n hookMap,\n isSolid: false,\n };\n};\n\n/**\n * High-level function to extract content from TS/JS/JSX/TSX AST.\n */\nexport const extractTsContent = (\n ast: t.File,\n fileCode: string,\n existingKeys: Set<string>,\n configuration: IntlayerConfig,\n filePath: string,\n unmergedDictionaries: Record<string, unknown> = {}\n): {\n extractedContent: Record<string, string>;\n replacements: BabelReplacement[];\n} => {\n const { extractedContent, replacements } = extractBabelContentForComponents(\n ast,\n fileCode,\n existingKeys,\n 'default',\n configuration,\n filePath,\n unmergedDictionaries\n );\n\n const flatContent: Record<string, string> = {};\n for (const group of Object.values(extractedContent)) {\n Object.assign(flatContent, group);\n }\n\n return { extractedContent: flatContent, replacements };\n};\n"],"mappings":"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"}
@@ -117,7 +117,7 @@ const extractContent = async (filePath, packageName, options) => {
117
117
  unmergedDictionaries,
118
118
  configuration
119
119
  }, saveComponent, dictionaryKey);
120
- if (!result || !result.extractedContentMap) {
120
+ if (!result?.extractedContentMap) {
121
121
  appLogger(`${colorize("Compiler:", ANSIColors.GREY_DARK)} No extractable text found in ${colorizePath(relative(baseDir, filePath))}`, { isVerbose: true });
122
122
  return;
123
123
  }
@@ -1 +1 @@
1
- {"version":3,"file":"extractContent.mjs","names":[],"sources":["../../../src/extractContent/extractContent.ts"],"sourcesContent":["import { execSync } from 'node:child_process';\nimport { readFileSync } from 'node:fs';\nimport { extname, relative } from 'node:path';\nimport type * as t from '@babel/types';\nimport { detectFormatCommand } from '@intlayer/chokidar/cli';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, colorizePath, getAppLogger } from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport { getProjectRequire } from '@intlayer/config/utils';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\nimport { extractTsContent } from './babelProcessor';\nimport { writeContentHelper } from './contentWriter';\nimport { processTsxFile } from './processTsxFile';\nimport {\n ATTRIBUTES_TO_EXTRACT,\n extractDictionaryKeyFromPath,\n type PackageName,\n shouldExtract,\n} from './utils';\nimport { extractDictionaryKey } from './utils/extractDictionaryKey';\nimport { generateKey } from './utils/generateKey';\n\nexport type ExtractIntlayerOptions = {\n configOptions?: GetConfigurationOptions;\n codeOnly?: boolean;\n declarationOnly?: boolean;\n unmergedDictionaries?: Record<string, unknown>;\n configuration?: IntlayerConfig;\n code?: string;\n onExtract?: (result: {\n key: string;\n content: Record<string, string>;\n filePath: string;\n }) => void | Promise<void>;\n};\n\ntype ExternalCompilerResult = {\n extractedContent: Record<string, string>;\n code: string;\n};\n\ntype ExternalCompilerOptions = {\n generateKey: typeof generateKey;\n shouldExtract: typeof shouldExtract;\n attributesToExtract: typeof ATTRIBUTES_TO_EXTRACT;\n extractDictionaryKeyFromPath: typeof extractDictionaryKeyFromPath;\n extractTsContent: (\n ast: unknown,\n code: string,\n keys: Set<string>,\n config: IntlayerConfig,\n path: string\n ) => ReturnType<typeof extractTsContent>;\n};\n\ntype VueCompiler = typeof import('@intlayer/vue-compiler');\ntype SvelteCompiler = typeof import('@intlayer/svelte-compiler');\n\n// Module caches\nlet vueCompiler: VueCompiler | null = null;\nlet svelteCompiler: SvelteCompiler | null = null;\n\ntype InternalExtractResult = {\n extractedContentMap: Record<string, Record<string, string>> | null;\n transformedCode: string | null;\n};\n\nconst formatCompilerResult = (\n componentKey: string,\n res?: ExternalCompilerResult\n): InternalExtractResult | undefined => {\n if (!res) return undefined;\n\n return {\n extractedContentMap: { [componentKey]: res.extractedContent },\n transformedCode: res.code,\n };\n};\n\ntype Dependencies = {\n vueCompiler: VueCompiler | null;\n svelteCompiler: SvelteCompiler | null;\n unmergedDictionaries: Record<string, unknown>;\n configuration: IntlayerConfig;\n};\n\nconst processFileInternal = (\n filePath: string,\n packageName: PackageName,\n options: ExtractIntlayerOptions | undefined,\n dependencies: Dependencies,\n saveComponent: boolean,\n providedComponentKey?: string\n): InternalExtractResult | undefined => {\n const fileText = options?.code ?? readFileSync(filePath, 'utf-8');\n const componentKey =\n providedComponentKey ??\n extractDictionaryKey(\n filePath,\n fileText,\n dependencies.configuration.compiler.dictionaryKeyPrefix\n );\n const ext = extname(filePath);\n\n const { vueCompiler, svelteCompiler, unmergedDictionaries, configuration } =\n dependencies;\n\n const compilerCommonOptions: ExternalCompilerOptions = {\n generateKey,\n shouldExtract,\n attributesToExtract: ATTRIBUTES_TO_EXTRACT,\n extractDictionaryKeyFromPath,\n extractTsContent: (ast, code, keys, config, path) =>\n extractTsContent(\n ast as t.File,\n code,\n keys,\n config,\n path,\n unmergedDictionaries\n ),\n };\n\n if (ext === '.vue') {\n if (!vueCompiler) {\n throw new Error(\n `Please install ${colorizePath('@intlayer/vue-compiler', ANSIColors.YELLOW)} to process Vue files.`\n );\n }\n\n const res = vueCompiler.processVueFile(\n filePath,\n componentKey,\n packageName,\n compilerCommonOptions,\n saveComponent\n );\n\n if (res) {\n return formatCompilerResult(componentKey, res);\n }\n }\n\n if (ext === '.svelte') {\n if (!svelteCompiler) {\n throw new Error(\n `Please install ${colorizePath('@intlayer/svelte-compiler', ANSIColors.YELLOW)} to process Svelte files.`\n );\n }\n\n const res = svelteCompiler.processSvelteFile(\n filePath,\n componentKey,\n packageName,\n compilerCommonOptions,\n saveComponent\n );\n\n if (res) {\n return formatCompilerResult(componentKey, res);\n }\n }\n\n if (['.tsx', '.jsx', '.ts', '.js', '.cjs', '.mjs'].includes(ext)) {\n const result = processTsxFile(\n filePath,\n componentKey,\n packageName,\n configuration,\n saveComponent,\n unmergedDictionaries,\n fileText\n );\n\n if (result) {\n return {\n extractedContentMap: result.extractedContent,\n transformedCode: result.modifiedCode,\n };\n }\n }\n\n return undefined;\n};\n\nconst handleExtractionSideEffects = async (\n extractedContentMap: Record<string, Record<string, string>>,\n filePath: string,\n options: ExtractIntlayerOptions | undefined,\n {\n configuration,\n baseDir,\n appLogger,\n }: {\n configuration: IntlayerConfig;\n baseDir: string;\n appLogger: ReturnType<typeof getAppLogger>;\n },\n saveComponent: boolean\n) => {\n if (options?.onExtract) {\n for (const [key, content] of Object.entries(extractedContentMap)) {\n await options.onExtract({ key, content, filePath });\n }\n }\n\n const writeContent = !options?.codeOnly && !options?.onExtract;\n\n if (writeContent) {\n for (const [key, content] of Object.entries(extractedContentMap)) {\n const contentFilePath = await writeContentHelper(\n content,\n key,\n filePath,\n configuration\n );\n\n const relativeContentFilePath = relative(baseDir, contentFilePath);\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} Created content file: ${colorizePath(relativeContentFilePath)}`\n );\n }\n }\n\n if (saveComponent) {\n const formatCommand = detectFormatCommand(configuration);\n\n if (formatCommand) {\n try {\n execSync(formatCommand.replace('{{file}}', filePath), {\n stdio: 'inherit',\n cwd: baseDir,\n });\n } catch (error) {\n console.error(error);\n }\n }\n\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} Updated component: ${colorizePath(relative(baseDir, filePath))}`\n );\n }\n};\n\ntype ExtractResult = {\n transformedCode: string | null;\n extractedContentMap: Record<string, Record<string, string>>;\n};\n\ntype ExtractContext = {\n configuration: IntlayerConfig;\n appLogger: ReturnType<typeof getAppLogger>;\n baseDir: string;\n unmergedDictionaries: Record<string, unknown>;\n saveComponent: boolean;\n componentExtension: string;\n};\n\nconst buildContext = (\n filePath: string,\n options: ExtractIntlayerOptions | undefined\n): ExtractContext => {\n const configuration =\n options?.configuration ?? getConfiguration(options?.configOptions);\n const appLogger = getAppLogger(configuration);\n const { baseDir } = configuration.system;\n const unmergedDictionaries =\n options?.unmergedDictionaries ?? getUnmergedDictionaries(configuration);\n const saveComponent = !options?.declarationOnly;\n const componentExtension = extname(filePath);\n\n return {\n configuration,\n appLogger,\n baseDir,\n unmergedDictionaries,\n saveComponent,\n componentExtension,\n };\n};\n\nexport const extractContent = async (\n filePath: string,\n packageName: PackageName,\n options?: ExtractIntlayerOptions\n): Promise<ExtractResult | undefined> => {\n const {\n configuration,\n appLogger,\n baseDir,\n unmergedDictionaries,\n saveComponent,\n componentExtension,\n } = buildContext(filePath, options);\n\n if (componentExtension === '.vue' && !vueCompiler) {\n try {\n vueCompiler = await import('@intlayer/vue-compiler');\n } catch {\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} Install ${colorizePath('@intlayer/vue-compiler', ANSIColors.YELLOW)} to process Vue files.`\n );\n }\n }\n\n if (componentExtension === '.svelte' && !svelteCompiler) {\n try {\n svelteCompiler = await import('@intlayer/svelte-compiler');\n } catch {\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} Install ${colorizePath('@intlayer/svelte-compiler', ANSIColors.YELLOW)} to process Svelte files.`\n );\n }\n }\n\n const fileText = options?.code ?? readFileSync(filePath, 'utf-8');\n const dictionaryKey = extractDictionaryKey(\n filePath,\n fileText,\n configuration.compiler.dictionaryKeyPrefix\n );\n\n const result = processFileInternal(\n filePath,\n packageName,\n options,\n { vueCompiler, svelteCompiler, unmergedDictionaries, configuration },\n saveComponent,\n dictionaryKey\n );\n\n if (!result || !result.extractedContentMap) {\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} No extractable text found in ${colorizePath(relative(baseDir, filePath))}`,\n { isVerbose: true }\n );\n return undefined;\n }\n\n await handleExtractionSideEffects(\n result.extractedContentMap,\n filePath,\n options,\n { configuration, baseDir, appLogger },\n saveComponent\n );\n\n return {\n transformedCode: result.transformedCode,\n extractedContentMap: result.extractedContentMap,\n };\n};\n\n/**\n * Synchronous variant of `extractContent` — used by the Babel plugin which\n * runs in a sync transform context (e.g. Next.js / Webpack).\n *\n * Differences from `extractContent`:\n * - Loads external compilers (Vue, Svelte) via `require()` instead of `import()`\n * - Fires `onExtract` callbacks as fire-and-forget (cannot await in sync context)\n * - Does NOT write dictionary files or run the code formatter; callers that\n * need persistence should supply an `onExtract` callback\n */\nexport const extractContentSync = (\n filePath: string,\n packageName: PackageName,\n options?: ExtractIntlayerOptions\n): ExtractResult | undefined => {\n const {\n configuration,\n appLogger,\n baseDir,\n unmergedDictionaries,\n saveComponent,\n componentExtension,\n } = buildContext(filePath, options);\n\n const requireFn = getProjectRequire();\n\n if (componentExtension === '.vue' && !vueCompiler) {\n try {\n vueCompiler = requireFn('@intlayer/vue-compiler') as VueCompiler;\n } catch {\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} Install ${colorizePath('@intlayer/vue-compiler', ANSIColors.YELLOW)} to process Vue files.`\n );\n }\n }\n\n if (componentExtension === '.svelte' && !svelteCompiler) {\n try {\n svelteCompiler = requireFn('@intlayer/svelte-compiler') as SvelteCompiler;\n } catch {\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} Install ${colorizePath('@intlayer/svelte-compiler', ANSIColors.YELLOW)} to process Svelte files.`\n );\n }\n }\n\n const result = processFileInternal(\n filePath,\n packageName,\n options,\n { vueCompiler, svelteCompiler, unmergedDictionaries, configuration },\n saveComponent\n );\n\n if (!result?.extractedContentMap) {\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} No extractable text found in ${colorizePath(relative(baseDir, filePath))}`,\n { isVerbose: true }\n );\n return undefined;\n }\n\n const { extractedContentMap, transformedCode } = result;\n\n if (options?.onExtract) {\n for (const [key, content] of Object.entries(extractedContentMap)) {\n void options.onExtract({ key, content, filePath });\n }\n }\n\n return { transformedCode, extractedContentMap };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"extractContent.mjs","names":[],"sources":["../../../src/extractContent/extractContent.ts"],"sourcesContent":["import { execSync } from 'node:child_process';\nimport { readFileSync } from 'node:fs';\nimport { extname, relative } from 'node:path';\nimport type * as t from '@babel/types';\nimport { detectFormatCommand } from '@intlayer/chokidar/cli';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, colorizePath, getAppLogger } from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport { getProjectRequire } from '@intlayer/config/utils';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\nimport { extractTsContent } from './babelProcessor';\nimport { writeContentHelper } from './contentWriter';\nimport { processTsxFile } from './processTsxFile';\nimport {\n ATTRIBUTES_TO_EXTRACT,\n extractDictionaryKeyFromPath,\n type PackageName,\n shouldExtract,\n} from './utils';\nimport { extractDictionaryKey } from './utils/extractDictionaryKey';\nimport { generateKey } from './utils/generateKey';\n\nexport type ExtractIntlayerOptions = {\n configOptions?: GetConfigurationOptions;\n codeOnly?: boolean;\n declarationOnly?: boolean;\n unmergedDictionaries?: Record<string, unknown>;\n configuration?: IntlayerConfig;\n code?: string;\n onExtract?: (result: {\n key: string;\n content: Record<string, string>;\n filePath: string;\n }) => void | Promise<void>;\n};\n\ntype ExternalCompilerResult = {\n extractedContent: Record<string, string>;\n code: string;\n};\n\ntype ExternalCompilerOptions = {\n generateKey: typeof generateKey;\n shouldExtract: typeof shouldExtract;\n attributesToExtract: typeof ATTRIBUTES_TO_EXTRACT;\n extractDictionaryKeyFromPath: typeof extractDictionaryKeyFromPath;\n extractTsContent: (\n ast: unknown,\n code: string,\n keys: Set<string>,\n config: IntlayerConfig,\n path: string\n ) => ReturnType<typeof extractTsContent>;\n};\n\ntype VueCompiler = typeof import('@intlayer/vue-compiler');\ntype SvelteCompiler = typeof import('@intlayer/svelte-compiler');\n\n// Module caches\nlet vueCompiler: VueCompiler | null = null;\nlet svelteCompiler: SvelteCompiler | null = null;\n\ntype InternalExtractResult = {\n extractedContentMap: Record<string, Record<string, string>> | null;\n transformedCode: string | null;\n};\n\nconst formatCompilerResult = (\n componentKey: string,\n res?: ExternalCompilerResult\n): InternalExtractResult | undefined => {\n if (!res) return undefined;\n\n return {\n extractedContentMap: { [componentKey]: res.extractedContent },\n transformedCode: res.code,\n };\n};\n\ntype Dependencies = {\n vueCompiler: VueCompiler | null;\n svelteCompiler: SvelteCompiler | null;\n unmergedDictionaries: Record<string, unknown>;\n configuration: IntlayerConfig;\n};\n\nconst processFileInternal = (\n filePath: string,\n packageName: PackageName,\n options: ExtractIntlayerOptions | undefined,\n dependencies: Dependencies,\n saveComponent: boolean,\n providedComponentKey?: string\n): InternalExtractResult | undefined => {\n const fileText = options?.code ?? readFileSync(filePath, 'utf-8');\n const componentKey =\n providedComponentKey ??\n extractDictionaryKey(\n filePath,\n fileText,\n dependencies.configuration.compiler.dictionaryKeyPrefix\n );\n const ext = extname(filePath);\n\n const { vueCompiler, svelteCompiler, unmergedDictionaries, configuration } =\n dependencies;\n\n const compilerCommonOptions: ExternalCompilerOptions = {\n generateKey,\n shouldExtract,\n attributesToExtract: ATTRIBUTES_TO_EXTRACT,\n extractDictionaryKeyFromPath,\n extractTsContent: (ast, code, keys, config, path) =>\n extractTsContent(\n ast as t.File,\n code,\n keys,\n config,\n path,\n unmergedDictionaries\n ),\n };\n\n if (ext === '.vue') {\n if (!vueCompiler) {\n throw new Error(\n `Please install ${colorizePath('@intlayer/vue-compiler', ANSIColors.YELLOW)} to process Vue files.`\n );\n }\n\n const res = vueCompiler.processVueFile(\n filePath,\n componentKey,\n packageName,\n compilerCommonOptions,\n saveComponent\n );\n\n if (res) {\n return formatCompilerResult(componentKey, res);\n }\n }\n\n if (ext === '.svelte') {\n if (!svelteCompiler) {\n throw new Error(\n `Please install ${colorizePath('@intlayer/svelte-compiler', ANSIColors.YELLOW)} to process Svelte files.`\n );\n }\n\n const res = svelteCompiler.processSvelteFile(\n filePath,\n componentKey,\n packageName,\n compilerCommonOptions,\n saveComponent\n );\n\n if (res) {\n return formatCompilerResult(componentKey, res);\n }\n }\n\n if (['.tsx', '.jsx', '.ts', '.js', '.cjs', '.mjs'].includes(ext)) {\n const result = processTsxFile(\n filePath,\n componentKey,\n packageName,\n configuration,\n saveComponent,\n unmergedDictionaries,\n fileText\n );\n\n if (result) {\n return {\n extractedContentMap: result.extractedContent,\n transformedCode: result.modifiedCode,\n };\n }\n }\n\n return undefined;\n};\n\nconst handleExtractionSideEffects = async (\n extractedContentMap: Record<string, Record<string, string>>,\n filePath: string,\n options: ExtractIntlayerOptions | undefined,\n {\n configuration,\n baseDir,\n appLogger,\n }: {\n configuration: IntlayerConfig;\n baseDir: string;\n appLogger: ReturnType<typeof getAppLogger>;\n },\n saveComponent: boolean\n) => {\n if (options?.onExtract) {\n for (const [key, content] of Object.entries(extractedContentMap)) {\n await options.onExtract({ key, content, filePath });\n }\n }\n\n const writeContent = !options?.codeOnly && !options?.onExtract;\n\n if (writeContent) {\n for (const [key, content] of Object.entries(extractedContentMap)) {\n const contentFilePath = await writeContentHelper(\n content,\n key,\n filePath,\n configuration\n );\n\n const relativeContentFilePath = relative(baseDir, contentFilePath);\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} Created content file: ${colorizePath(relativeContentFilePath)}`\n );\n }\n }\n\n if (saveComponent) {\n const formatCommand = detectFormatCommand(configuration);\n\n if (formatCommand) {\n try {\n execSync(formatCommand.replace('{{file}}', filePath), {\n stdio: 'inherit',\n cwd: baseDir,\n });\n } catch (error) {\n console.error(error);\n }\n }\n\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} Updated component: ${colorizePath(relative(baseDir, filePath))}`\n );\n }\n};\n\ntype ExtractResult = {\n transformedCode: string | null;\n extractedContentMap: Record<string, Record<string, string>>;\n};\n\ntype ExtractContext = {\n configuration: IntlayerConfig;\n appLogger: ReturnType<typeof getAppLogger>;\n baseDir: string;\n unmergedDictionaries: Record<string, unknown>;\n saveComponent: boolean;\n componentExtension: string;\n};\n\nconst buildContext = (\n filePath: string,\n options: ExtractIntlayerOptions | undefined\n): ExtractContext => {\n const configuration =\n options?.configuration ?? getConfiguration(options?.configOptions);\n const appLogger = getAppLogger(configuration);\n const { baseDir } = configuration.system;\n const unmergedDictionaries =\n options?.unmergedDictionaries ?? getUnmergedDictionaries(configuration);\n const saveComponent = !options?.declarationOnly;\n const componentExtension = extname(filePath);\n\n return {\n configuration,\n appLogger,\n baseDir,\n unmergedDictionaries,\n saveComponent,\n componentExtension,\n };\n};\n\nexport const extractContent = async (\n filePath: string,\n packageName: PackageName,\n options?: ExtractIntlayerOptions\n): Promise<ExtractResult | undefined> => {\n const {\n configuration,\n appLogger,\n baseDir,\n unmergedDictionaries,\n saveComponent,\n componentExtension,\n } = buildContext(filePath, options);\n\n if (componentExtension === '.vue' && !vueCompiler) {\n try {\n vueCompiler = await import('@intlayer/vue-compiler');\n } catch {\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} Install ${colorizePath('@intlayer/vue-compiler', ANSIColors.YELLOW)} to process Vue files.`\n );\n }\n }\n\n if (componentExtension === '.svelte' && !svelteCompiler) {\n try {\n svelteCompiler = await import('@intlayer/svelte-compiler');\n } catch {\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} Install ${colorizePath('@intlayer/svelte-compiler', ANSIColors.YELLOW)} to process Svelte files.`\n );\n }\n }\n\n const fileText = options?.code ?? readFileSync(filePath, 'utf-8');\n const dictionaryKey = extractDictionaryKey(\n filePath,\n fileText,\n configuration.compiler.dictionaryKeyPrefix\n );\n\n const result = processFileInternal(\n filePath,\n packageName,\n options,\n { vueCompiler, svelteCompiler, unmergedDictionaries, configuration },\n saveComponent,\n dictionaryKey\n );\n\n if (!result?.extractedContentMap) {\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} No extractable text found in ${colorizePath(relative(baseDir, filePath))}`,\n { isVerbose: true }\n );\n return undefined;\n }\n\n await handleExtractionSideEffects(\n result.extractedContentMap,\n filePath,\n options,\n { configuration, baseDir, appLogger },\n saveComponent\n );\n\n return {\n transformedCode: result.transformedCode,\n extractedContentMap: result.extractedContentMap,\n };\n};\n\n/**\n * Synchronous variant of `extractContent` — used by the Babel plugin which\n * runs in a sync transform context (e.g. Next.js / Webpack).\n *\n * Differences from `extractContent`:\n * - Loads external compilers (Vue, Svelte) via `require()` instead of `import()`\n * - Fires `onExtract` callbacks as fire-and-forget (cannot await in sync context)\n * - Does NOT write dictionary files or run the code formatter; callers that\n * need persistence should supply an `onExtract` callback\n */\nexport const extractContentSync = (\n filePath: string,\n packageName: PackageName,\n options?: ExtractIntlayerOptions\n): ExtractResult | undefined => {\n const {\n configuration,\n appLogger,\n baseDir,\n unmergedDictionaries,\n saveComponent,\n componentExtension,\n } = buildContext(filePath, options);\n\n const requireFn = getProjectRequire();\n\n if (componentExtension === '.vue' && !vueCompiler) {\n try {\n vueCompiler = requireFn('@intlayer/vue-compiler') as VueCompiler;\n } catch {\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} Install ${colorizePath('@intlayer/vue-compiler', ANSIColors.YELLOW)} to process Vue files.`\n );\n }\n }\n\n if (componentExtension === '.svelte' && !svelteCompiler) {\n try {\n svelteCompiler = requireFn('@intlayer/svelte-compiler') as SvelteCompiler;\n } catch {\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} Install ${colorizePath('@intlayer/svelte-compiler', ANSIColors.YELLOW)} to process Svelte files.`\n );\n }\n }\n\n const result = processFileInternal(\n filePath,\n packageName,\n options,\n { vueCompiler, svelteCompiler, unmergedDictionaries, configuration },\n saveComponent\n );\n\n if (!result?.extractedContentMap) {\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} No extractable text found in ${colorizePath(relative(baseDir, filePath))}`,\n { isVerbose: true }\n );\n return undefined;\n }\n\n const { extractedContentMap, transformedCode } = result;\n\n if (options?.onExtract) {\n for (const [key, content] of Object.entries(extractedContentMap)) {\n void options.onExtract({ key, content, filePath });\n }\n }\n\n return { transformedCode, extractedContentMap };\n};\n"],"mappings":"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"}
@@ -96,6 +96,20 @@ const processTsxFile = (filePath, componentKey, packageName, configuration, save
96
96
  end,
97
97
  replacement: accessStr
98
98
  });
99
+ } else if (type === "template-literal" && path.isTemplateLiteral()) {
100
+ let replacement = `${contentAccessCode}`;
101
+ if (variables && variables.length > 0) {
102
+ const objProps = variables.map((variableString) => {
103
+ const [key, variableValue] = variableString.split(":").map((part) => part.trim());
104
+ return `${key}: ${variableValue}`;
105
+ }).join(", ");
106
+ replacement += `({ ${objProps} })`;
107
+ } else replacement += valueSuffix;
108
+ textEdits.push({
109
+ start: path.node.start,
110
+ end: path.node.end,
111
+ replacement
112
+ });
99
113
  }
100
114
  }
101
115
  let needsUseIntlayer = false;
@@ -133,8 +147,19 @@ const processTsxFile = (filePath, componentKey, packageName, configuration, save
133
147
  const hook = hookMap.get(componentPath.node) || "useIntlayer";
134
148
  if (hook === "useIntlayer") needsUseIntlayer = true;
135
149
  if (hook === "getIntlayer") needsGetIntlayer = true;
136
- const bodyPath = componentPath.get("body");
137
150
  const hookStatementStr = `\n const content = ${hook}('${finalKey}');\n`;
151
+ if (componentPath.isProgram()) {
152
+ let insertPos = 0;
153
+ if (componentPath.node.directives.length > 0) insertPos = componentPath.node.directives[componentPath.node.directives.length - 1].end;
154
+ for (const stmt of componentPath.node.body) if (t.isImportDeclaration(stmt)) insertPos = Math.max(insertPos, stmt.end);
155
+ textEdits.push({
156
+ start: insertPos,
157
+ end: insertPos,
158
+ replacement: `\n${hookStatementStr}`
159
+ });
160
+ continue;
161
+ }
162
+ const bodyPath = componentPath.get("body");
138
163
  if (bodyPath.isBlockStatement()) textEdits.push({
139
164
  start: bodyPath.node.start + 1,
140
165
  end: bodyPath.node.start + 1,
@@ -1 +1 @@
1
- {"version":3,"file":"processTsxFile.mjs","names":[],"sources":["../../../src/extractContent/processTsxFile.ts"],"sourcesContent":["import { execSync } from 'node:child_process';\nimport { readFileSync, writeFileSync } from 'node:fs';\nimport { parse } from '@babel/parser';\nimport _traverse, { type NodePath } from '@babel/traverse';\nimport * as t from '@babel/types';\nimport { detectFormatCommand } from '@intlayer/chokidar/cli';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { extractBabelContentForComponents } from './babelProcessor';\nimport {\n type ExistingIntlayerInfo,\n getExistingIntlayerInfo,\n type PackageName,\n SERVER_CAPABLE_PACKAGES,\n} from './utils';\n\nexport type TextEdit = {\n start: number;\n end: number;\n replacement: string;\n};\n\nconst traverse = (\n typeof _traverse === 'function' ? _traverse : (_traverse as any).default\n) as typeof _traverse;\n\n/**\n * Processes a TSX/JSX/TS/JS file to extract content and inject Intlayer hooks/calls.\n */\nexport const processTsxFile = (\n filePath: string,\n componentKey: string,\n packageName: PackageName,\n configuration: IntlayerConfig,\n save: boolean = true,\n unmergedDictionaries: Record<string, unknown> = {},\n providedFileCode?: string\n): {\n extractedContent: Record<string, Record<string, string>>;\n modifiedCode: string;\n} | null => {\n const fileCode = providedFileCode ?? readFileSync(filePath, 'utf-8');\n\n const ast = parse(fileCode, {\n sourceType: 'module',\n plugins: ['jsx', 'typescript'],\n });\n\n const hasUseClient = ast.program.directives.some(\n (directive) => directive.value.value === 'use client'\n );\n\n const effectivePackageName =\n SERVER_CAPABLE_PACKAGES.has(packageName) && !hasUseClient\n ? `${packageName}/server`\n : packageName;\n\n // Both Solid and Angular expose content via a reactive/signal function —\n // access is `content().key` rather than `content.key`.\n const isSolid =\n packageName === 'solid-intlayer' || packageName === 'angular-intlayer';\n const existingKeys = new Set<string>();\n\n const {\n extractedContent,\n replacements,\n componentsNeedingHooks,\n componentKeyMap,\n componentPaths,\n hookMap,\n } = extractBabelContentForComponents(\n ast,\n fileCode,\n existingKeys,\n componentKey,\n configuration,\n filePath,\n unmergedDictionaries\n );\n\n if (Object.keys(extractedContent).length === 0) return null;\n\n const textEdits: TextEdit[] = [];\n\n // Build a lookup map: component AST node → NodePath (O(1) access)\n const componentNodeToPath = new Map<t.Node, NodePath>();\n for (const componentPath of componentPaths) {\n componentNodeToPath.set(componentPath.node, componentPath);\n }\n\n // Cache getExistingIntlayerInfo results for all component paths (avoids repeated traversals)\n const existingInfoCache = new Map<t.Node, ExistingIntlayerInfo | undefined>();\n for (const componentPath of componentPaths) {\n existingInfoCache.set(\n componentPath.node,\n getExistingIntlayerInfo(componentPath)\n );\n }\n\n /**\n * Walks up the ancestor chain to find the nearest enclosing component's\n * existing Intlayer info (if any).\n */\n const getExistingInfoForPath = (\n path: NodePath\n ): ExistingIntlayerInfo | undefined => {\n let current: NodePath | null = path;\n while (current) {\n if (componentNodeToPath.has(current.node)) {\n return existingInfoCache.get(current.node);\n }\n current = current.parentPath;\n }\n return undefined;\n };\n\n const getProvidingHookType = (\n path: NodePath\n ): 'useIntlayer' | 'getIntlayer' => {\n let current: NodePath | null = path;\n while (current) {\n const componentPath = componentNodeToPath.get(current.node);\n\n if (componentPath) {\n const existingInfo = existingInfoCache.get(componentPath.node);\n\n if (existingInfo) {\n return existingInfo.hook;\n }\n\n if (componentsNeedingHooks.has(componentPath)) {\n return hookMap.get(componentPath.node) || 'useIntlayer';\n }\n }\n current = current.parentPath;\n }\n return 'useIntlayer';\n };\n\n for (const {\n path,\n key,\n type,\n variables,\n childrenToReplace,\n } of replacements) {\n const existingInfo = getExistingInfoForPath(path);\n // When the existing call is destructured (e.g. `const { a } = getIntlayer(...)`),\n // new keys are added directly to that destructuring, so access them by name alone.\n const varName = existingInfo?.variableName ?? 'content';\n const contentAccessCode = existingInfo?.isDestructured\n ? key\n : isSolid\n ? `${varName}().${key}`\n : `${varName}.${key}`;\n const hookType = getProvidingHookType(path);\n const valueSuffix = hookType === 'getIntlayer' ? '' : '.value';\n\n if (type === 'jsx-text' && path.isJSXText()) {\n textEdits.push({\n start: path.node.start!,\n end: path.node.end!,\n replacement: `{${contentAccessCode}}`,\n });\n } else if (type === 'jsx-attribute' && path.isJSXAttribute()) {\n const valNode = path.node.value;\n\n if (valNode) {\n textEdits.push({\n start: valNode.start!,\n end: valNode.end!,\n replacement: `{${contentAccessCode}${valueSuffix}}`,\n });\n }\n } else if (type === 'string-literal' && path.isStringLiteral()) {\n textEdits.push({\n start: path.node.start!,\n end: path.node.end!,\n replacement: `${contentAccessCode}${valueSuffix}`,\n });\n } else if (\n type === 'jsx-text-combined' &&\n childrenToReplace &&\n childrenToReplace.length > 0\n ) {\n const accessStr = `{${contentAccessCode}}`;\n const start = childrenToReplace[0].start!;\n const end = childrenToReplace[childrenToReplace.length - 1].end!;\n textEdits.push({ start, end, replacement: accessStr });\n } else if (\n type === 'jsx-insertion' &&\n variables &&\n childrenToReplace &&\n childrenToReplace.length > 0\n ) {\n const objProps = variables\n .map((variableString) => {\n const [key, variableValue] = variableString\n .split(':')\n .map((part) => part.trim());\n return `${key}: ${variableValue}`;\n })\n .join(', ');\n\n const accessStr = `{${contentAccessCode}({ ${objProps} })}`;\n const start = childrenToReplace[0].start!;\n const end = childrenToReplace[childrenToReplace.length - 1].end!;\n textEdits.push({ start, end, replacement: accessStr });\n }\n }\n\n let needsUseIntlayer = false;\n let needsGetIntlayer = false;\n\n for (const componentPath of componentsNeedingHooks) {\n const finalKey = componentKeyMap.get(componentPath.node)!;\n const existingInfo = existingInfoCache.get(componentPath.node);\n\n if (existingInfo) {\n if (existingInfo.hook === 'useIntlayer') needsUseIntlayer = true;\n\n if (existingInfo.hook === 'getIntlayer') needsGetIntlayer = true;\n\n // When the existing call is destructured, inject any missing keys into\n // the destructuring pattern so they can be accessed by name directly.\n if (existingInfo.isDestructured && existingInfo.objectPatternNode) {\n const neededKeys = new Set<string>();\n\n for (const { path: rPath, key: rKey } of replacements) {\n let current: NodePath | null = rPath;\n\n while (current) {\n if (current.node === componentPath.node) {\n neededKeys.add(rKey);\n break;\n }\n current = current.parentPath;\n }\n }\n\n const missingKeys = [...neededKeys].filter(\n (key) => !existingInfo.existingDestructuredKeys.includes(key)\n );\n\n if (missingKeys.length > 0) {\n const { objectPatternNode } = existingInfo;\n // Insert right after the last property so the space/newline before\n // `}` is naturally preserved: `{ a }` → `{ a, b }`.\n const lastProp =\n objectPatternNode.properties[\n objectPatternNode.properties.length - 1\n ];\n textEdits.push({\n start: lastProp.end!,\n end: lastProp.end!,\n replacement: `, ${missingKeys.join(', ')}`,\n });\n }\n }\n } else {\n const hook = hookMap.get(componentPath.node) || 'useIntlayer';\n\n if (hook === 'useIntlayer') needsUseIntlayer = true;\n\n if (hook === 'getIntlayer') needsGetIntlayer = true;\n\n const bodyPath = componentPath.get('body') as NodePath;\n const hookStatementStr = `\\n const content = ${hook}('${finalKey}');\\n`;\n\n if (bodyPath.isBlockStatement()) {\n textEdits.push({\n start: bodyPath.node.start! + 1,\n end: bodyPath.node.start! + 1,\n replacement: hookStatementStr,\n });\n } else if (bodyPath.isExpression()) {\n const start = bodyPath.node.start!;\n const end = bodyPath.node.end!;\n\n // Detect wrapping parentheses: `() => (expr)` — scan backward from\n // the expression start and forward from its end for matching `(` / `)`.\n let parenStart = -1;\n let parenEnd = -1;\n\n for (let i = start - 1; i >= 0; i--) {\n const char = fileCode[i];\n if (char === '(') {\n parenStart = i;\n break;\n }\n if (char !== ' ' && char !== '\\n' && char !== '\\r' && char !== '\\t')\n break;\n }\n\n if (parenStart !== -1) {\n for (let i = end; i < fileCode.length; i++) {\n const char = fileCode[i];\n if (char === ')') {\n parenEnd = i;\n break;\n }\n if (char !== ' ' && char !== '\\n' && char !== '\\r' && char !== '\\t')\n break;\n }\n }\n\n if (parenStart !== -1 && parenEnd !== -1) {\n // Replace the surrounding `(` … `)` with a block statement. We keep\n // the parentheses as `return (…)` to prevent automatic semicolon\n // insertion when the returned expression starts on a new line.\n textEdits.push({\n start: parenEnd,\n end: parenEnd + 1,\n replacement: `)\\n}`,\n });\n textEdits.push({\n start: parenStart,\n end: parenStart + 1,\n replacement: `{\\n const content = ${hook}('${finalKey}');\\n return (`,\n });\n } else {\n textEdits.push({\n start: end,\n end: end,\n replacement: `\\n}`,\n });\n textEdits.push({\n start: start,\n end: start,\n replacement: `{\\n const content = ${hook}('${finalKey}');\\n return `,\n });\n }\n }\n }\n }\n\n const injectImport = (hookName: string, targetPackage: string) => {\n let existingImportPath: NodePath<t.ImportDeclaration> | undefined;\n\n traverse(ast, {\n ImportDeclaration(path) {\n if (path.node.source.value === targetPackage) {\n existingImportPath = path;\n path.stop();\n }\n },\n });\n\n if (!existingImportPath) {\n const newImportStr = `import { ${hookName} } from '${targetPackage}';\\n`;\n let insertPos = 0;\n\n if (ast.program.body.length > 0) {\n insertPos = ast.program.body[0].start!;\n } else if (ast.program.directives && ast.program.directives.length > 0) {\n insertPos =\n ast.program.directives[ast.program.directives.length - 1].end!;\n\n if (fileCode[insertPos] === ';') insertPos++;\n\n textEdits.push({\n start: insertPos,\n end: insertPos,\n replacement: `\\n${newImportStr}`,\n });\n return;\n }\n\n if (\n insertPos === 0 ||\n (ast.program.body.length > 0 &&\n insertPos === ast.program.body[0].start!)\n ) {\n textEdits.push({\n start: insertPos,\n end: insertPos,\n replacement: newImportStr,\n });\n }\n } else {\n const existingSpecifiers = existingImportPath.node.specifiers;\n const missingImport = !existingSpecifiers.some(\n (specifier) =>\n t.isImportSpecifier(specifier) &&\n t.isIdentifier(specifier.imported) &&\n specifier.imported.name === hookName\n );\n\n if (missingImport) {\n const importCode = fileCode.slice(\n existingImportPath.node.start!,\n existingImportPath.node.end!\n );\n const closingBraceIndex = importCode.lastIndexOf('}');\n\n if (closingBraceIndex !== -1) {\n const isCommaNeeded = !importCode\n .slice(0, closingBraceIndex)\n .trim()\n .endsWith(',');\n const absolutePos =\n existingImportPath.node.start! + closingBraceIndex;\n const prefix = isCommaNeeded ? ', ' : ' ';\n textEdits.push({\n start: absolutePos,\n end: absolutePos,\n replacement: `${prefix}${hookName} `,\n });\n }\n }\n }\n };\n\n if (needsUseIntlayer) injectImport('useIntlayer', effectivePackageName);\n\n if (needsGetIntlayer) injectImport('getIntlayer', 'intlayer');\n\n textEdits.sort((editA, editB) => {\n if (editB.start !== editA.start) return editB.start - editA.start;\n\n return editB.end - editA.end;\n });\n\n let formattedCode = fileCode;\n\n for (const edit of textEdits) {\n formattedCode =\n formattedCode.slice(0, edit.start) +\n edit.replacement +\n formattedCode.slice(edit.end);\n }\n\n if (save) {\n writeFileSync(filePath, formattedCode);\n\n const formatCommand = detectFormatCommand(configuration);\n\n if (formatCommand) {\n try {\n execSync(formatCommand.replace('{{file}}', filePath), {\n stdio: 'inherit',\n cwd: configuration.system.baseDir,\n });\n } catch (error) {\n console.error(error);\n }\n }\n }\n\n return { extractedContent, modifiedCode: formattedCode };\n};\n"],"mappings":";;;;;;;;;;;AAqBA,MAAM,WACJ,OAAO,cAAc,aAAa,YAAa,UAAkB;;;;AAMnE,MAAa,kBACX,UACA,cACA,aACA,eACA,OAAgB,MAChB,uBAAgD,EAAE,EAClD,qBAIU;CACV,MAAM,WAAW,oBAAoB,aAAa,UAAU,QAAQ;CAEpE,MAAM,MAAM,MAAM,UAAU;EAC1B,YAAY;EACZ,SAAS,CAAC,OAAO,aAAa;EAC/B,CAAC;CAEF,MAAM,eAAe,IAAI,QAAQ,WAAW,MACzC,cAAc,UAAU,MAAM,UAAU,aAC1C;CAED,MAAM,uBACJ,wBAAwB,IAAI,YAAY,IAAI,CAAC,eACzC,GAAG,YAAY,WACf;CAIN,MAAM,UACJ,gBAAgB,oBAAoB,gBAAgB;CAGtD,MAAM,EACJ,kBACA,cACA,wBACA,iBACA,gBACA,YACE,iCACF,KACA,0BAXmB,IAAI,KAAa,EAapC,cACA,eACA,UACA,qBACD;AAED,KAAI,OAAO,KAAK,iBAAiB,CAAC,WAAW,EAAG,QAAO;CAEvD,MAAM,YAAwB,EAAE;CAGhC,MAAM,sCAAsB,IAAI,KAAuB;AACvD,MAAK,MAAM,iBAAiB,eAC1B,qBAAoB,IAAI,cAAc,MAAM,cAAc;CAI5D,MAAM,oCAAoB,IAAI,KAA+C;AAC7E,MAAK,MAAM,iBAAiB,eAC1B,mBAAkB,IAChB,cAAc,MACd,wBAAwB,cAAc,CACvC;;;;;CAOH,MAAM,0BACJ,SACqC;EACrC,IAAI,UAA2B;AAC/B,SAAO,SAAS;AACd,OAAI,oBAAoB,IAAI,QAAQ,KAAK,CACvC,QAAO,kBAAkB,IAAI,QAAQ,KAAK;AAE5C,aAAU,QAAQ;;;CAKtB,MAAM,wBACJ,SACkC;EAClC,IAAI,UAA2B;AAC/B,SAAO,SAAS;GACd,MAAM,gBAAgB,oBAAoB,IAAI,QAAQ,KAAK;AAE3D,OAAI,eAAe;IACjB,MAAM,eAAe,kBAAkB,IAAI,cAAc,KAAK;AAE9D,QAAI,aACF,QAAO,aAAa;AAGtB,QAAI,uBAAuB,IAAI,cAAc,CAC3C,QAAO,QAAQ,IAAI,cAAc,KAAK,IAAI;;AAG9C,aAAU,QAAQ;;AAEpB,SAAO;;AAGT,MAAK,MAAM,EACT,MACA,KACA,MACA,WACA,uBACG,cAAc;EACjB,MAAM,eAAe,uBAAuB,KAAK;EAGjD,MAAM,UAAU,cAAc,gBAAgB;EAC9C,MAAM,oBAAoB,cAAc,iBACpC,MACA,UACE,GAAG,QAAQ,KAAK,QAChB,GAAG,QAAQ,GAAG;EAEpB,MAAM,cADW,qBAAqB,KAAK,KACV,gBAAgB,KAAK;AAEtD,MAAI,SAAS,cAAc,KAAK,WAAW,CACzC,WAAU,KAAK;GACb,OAAO,KAAK,KAAK;GACjB,KAAK,KAAK,KAAK;GACf,aAAa,IAAI,kBAAkB;GACpC,CAAC;WACO,SAAS,mBAAmB,KAAK,gBAAgB,EAAE;GAC5D,MAAM,UAAU,KAAK,KAAK;AAE1B,OAAI,QACF,WAAU,KAAK;IACb,OAAO,QAAQ;IACf,KAAK,QAAQ;IACb,aAAa,IAAI,oBAAoB,YAAY;IAClD,CAAC;aAEK,SAAS,oBAAoB,KAAK,iBAAiB,CAC5D,WAAU,KAAK;GACb,OAAO,KAAK,KAAK;GACjB,KAAK,KAAK,KAAK;GACf,aAAa,GAAG,oBAAoB;GACrC,CAAC;WAEF,SAAS,uBACT,qBACA,kBAAkB,SAAS,GAC3B;GACA,MAAM,YAAY,IAAI,kBAAkB;GACxC,MAAM,QAAQ,kBAAkB,GAAG;GACnC,MAAM,MAAM,kBAAkB,kBAAkB,SAAS,GAAG;AAC5D,aAAU,KAAK;IAAE;IAAO;IAAK,aAAa;IAAW,CAAC;aAEtD,SAAS,mBACT,aACA,qBACA,kBAAkB,SAAS,GAC3B;GAUA,MAAM,YAAY,IAAI,kBAAkB,KATvB,UACd,KAAK,mBAAmB;IACvB,MAAM,CAAC,KAAK,iBAAiB,eAC1B,MAAM,IAAI,CACV,KAAK,SAAS,KAAK,MAAM,CAAC;AAC7B,WAAO,GAAG,IAAI,IAAI;KAClB,CACD,KAAK,KAAK,CAEyC;GACtD,MAAM,QAAQ,kBAAkB,GAAG;GACnC,MAAM,MAAM,kBAAkB,kBAAkB,SAAS,GAAG;AAC5D,aAAU,KAAK;IAAE;IAAO;IAAK,aAAa;IAAW,CAAC;;;CAI1D,IAAI,mBAAmB;CACvB,IAAI,mBAAmB;AAEvB,MAAK,MAAM,iBAAiB,wBAAwB;EAClD,MAAM,WAAW,gBAAgB,IAAI,cAAc,KAAK;EACxD,MAAM,eAAe,kBAAkB,IAAI,cAAc,KAAK;AAE9D,MAAI,cAAc;AAChB,OAAI,aAAa,SAAS,cAAe,oBAAmB;AAE5D,OAAI,aAAa,SAAS,cAAe,oBAAmB;AAI5D,OAAI,aAAa,kBAAkB,aAAa,mBAAmB;IACjE,MAAM,6BAAa,IAAI,KAAa;AAEpC,SAAK,MAAM,EAAE,MAAM,OAAO,KAAK,UAAU,cAAc;KACrD,IAAI,UAA2B;AAE/B,YAAO,SAAS;AACd,UAAI,QAAQ,SAAS,cAAc,MAAM;AACvC,kBAAW,IAAI,KAAK;AACpB;;AAEF,gBAAU,QAAQ;;;IAItB,MAAM,cAAc,CAAC,GAAG,WAAW,CAAC,QACjC,QAAQ,CAAC,aAAa,yBAAyB,SAAS,IAAI,CAC9D;AAED,QAAI,YAAY,SAAS,GAAG;KAC1B,MAAM,EAAE,sBAAsB;KAG9B,MAAM,WACJ,kBAAkB,WAChB,kBAAkB,WAAW,SAAS;AAE1C,eAAU,KAAK;MACb,OAAO,SAAS;MAChB,KAAK,SAAS;MACd,aAAa,KAAK,YAAY,KAAK,KAAK;MACzC,CAAC;;;SAGD;GACL,MAAM,OAAO,QAAQ,IAAI,cAAc,KAAK,IAAI;AAEhD,OAAI,SAAS,cAAe,oBAAmB;AAE/C,OAAI,SAAS,cAAe,oBAAmB;GAE/C,MAAM,WAAW,cAAc,IAAI,OAAO;GAC1C,MAAM,mBAAmB,uBAAuB,KAAK,IAAI,SAAS;AAElE,OAAI,SAAS,kBAAkB,CAC7B,WAAU,KAAK;IACb,OAAO,SAAS,KAAK,QAAS;IAC9B,KAAK,SAAS,KAAK,QAAS;IAC5B,aAAa;IACd,CAAC;YACO,SAAS,cAAc,EAAE;IAClC,MAAM,QAAQ,SAAS,KAAK;IAC5B,MAAM,MAAM,SAAS,KAAK;IAI1B,IAAI,aAAa;IACjB,IAAI,WAAW;AAEf,SAAK,IAAI,IAAI,QAAQ,GAAG,KAAK,GAAG,KAAK;KACnC,MAAM,OAAO,SAAS;AACtB,SAAI,SAAS,KAAK;AAChB,mBAAa;AACb;;AAEF,SAAI,SAAS,OAAO,SAAS,QAAQ,SAAS,QAAQ,SAAS,IAC7D;;AAGJ,QAAI,eAAe,GACjB,MAAK,IAAI,IAAI,KAAK,IAAI,SAAS,QAAQ,KAAK;KAC1C,MAAM,OAAO,SAAS;AACtB,SAAI,SAAS,KAAK;AAChB,iBAAW;AACX;;AAEF,SAAI,SAAS,OAAO,SAAS,QAAQ,SAAS,QAAQ,SAAS,IAC7D;;AAIN,QAAI,eAAe,MAAM,aAAa,IAAI;AAIxC,eAAU,KAAK;MACb,OAAO;MACP,KAAK,WAAW;MAChB,aAAa;MACd,CAAC;AACF,eAAU,KAAK;MACb,OAAO;MACP,KAAK,aAAa;MAClB,aAAa,wBAAwB,KAAK,IAAI,SAAS;MACxD,CAAC;WACG;AACL,eAAU,KAAK;MACb,OAAO;MACF;MACL,aAAa;MACd,CAAC;AACF,eAAU,KAAK;MACN;MACP,KAAK;MACL,aAAa,wBAAwB,KAAK,IAAI,SAAS;MACxD,CAAC;;;;;CAMV,MAAM,gBAAgB,UAAkB,kBAA0B;EAChE,IAAI;AAEJ,WAAS,KAAK,EACZ,kBAAkB,MAAM;AACtB,OAAI,KAAK,KAAK,OAAO,UAAU,eAAe;AAC5C,yBAAqB;AACrB,SAAK,MAAM;;KAGhB,CAAC;AAEF,MAAI,CAAC,oBAAoB;GACvB,MAAM,eAAe,YAAY,SAAS,WAAW,cAAc;GACnE,IAAI,YAAY;AAEhB,OAAI,IAAI,QAAQ,KAAK,SAAS,EAC5B,aAAY,IAAI,QAAQ,KAAK,GAAG;YACvB,IAAI,QAAQ,cAAc,IAAI,QAAQ,WAAW,SAAS,GAAG;AACtE,gBACE,IAAI,QAAQ,WAAW,IAAI,QAAQ,WAAW,SAAS,GAAG;AAE5D,QAAI,SAAS,eAAe,IAAK;AAEjC,cAAU,KAAK;KACb,OAAO;KACP,KAAK;KACL,aAAa,KAAK;KACnB,CAAC;AACF;;AAGF,OACE,cAAc,KACb,IAAI,QAAQ,KAAK,SAAS,KACzB,cAAc,IAAI,QAAQ,KAAK,GAAG,MAEpC,WAAU,KAAK;IACb,OAAO;IACP,KAAK;IACL,aAAa;IACd,CAAC;aAIkB,CADK,mBAAmB,KAAK,WACT,MACvC,cACC,EAAE,kBAAkB,UAAU,IAC9B,EAAE,aAAa,UAAU,SAAS,IAClC,UAAU,SAAS,SAAS,SAC/B,EAEkB;GACjB,MAAM,aAAa,SAAS,MAC1B,mBAAmB,KAAK,OACxB,mBAAmB,KAAK,IACzB;GACD,MAAM,oBAAoB,WAAW,YAAY,IAAI;AAErD,OAAI,sBAAsB,IAAI;IAC5B,MAAM,gBAAgB,CAAC,WACpB,MAAM,GAAG,kBAAkB,CAC3B,MAAM,CACN,SAAS,IAAI;IAChB,MAAM,cACJ,mBAAmB,KAAK,QAAS;IACnC,MAAM,SAAS,gBAAgB,OAAO;AACtC,cAAU,KAAK;KACb,OAAO;KACP,KAAK;KACL,aAAa,GAAG,SAAS,SAAS;KACnC,CAAC;;;;AAMV,KAAI,iBAAkB,cAAa,eAAe,qBAAqB;AAEvE,KAAI,iBAAkB,cAAa,eAAe,WAAW;AAE7D,WAAU,MAAM,OAAO,UAAU;AAC/B,MAAI,MAAM,UAAU,MAAM,MAAO,QAAO,MAAM,QAAQ,MAAM;AAE5D,SAAO,MAAM,MAAM,MAAM;GACzB;CAEF,IAAI,gBAAgB;AAEpB,MAAK,MAAM,QAAQ,UACjB,iBACE,cAAc,MAAM,GAAG,KAAK,MAAM,GAClC,KAAK,cACL,cAAc,MAAM,KAAK,IAAI;AAGjC,KAAI,MAAM;AACR,gBAAc,UAAU,cAAc;EAEtC,MAAM,gBAAgB,oBAAoB,cAAc;AAExD,MAAI,cACF,KAAI;AACF,YAAS,cAAc,QAAQ,YAAY,SAAS,EAAE;IACpD,OAAO;IACP,KAAK,cAAc,OAAO;IAC3B,CAAC;WACK,OAAO;AACd,WAAQ,MAAM,MAAM;;;AAK1B,QAAO;EAAE;EAAkB,cAAc;EAAe"}
1
+ {"version":3,"file":"processTsxFile.mjs","names":[],"sources":["../../../src/extractContent/processTsxFile.ts"],"sourcesContent":["import { execSync } from 'node:child_process';\nimport { readFileSync, writeFileSync } from 'node:fs';\nimport { parse } from '@babel/parser';\nimport _traverse, { type NodePath } from '@babel/traverse';\nimport * as t from '@babel/types';\nimport { detectFormatCommand } from '@intlayer/chokidar/cli';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { extractBabelContentForComponents } from './babelProcessor';\nimport {\n type ExistingIntlayerInfo,\n getExistingIntlayerInfo,\n type PackageName,\n SERVER_CAPABLE_PACKAGES,\n} from './utils';\n\nexport type TextEdit = {\n start: number;\n end: number;\n replacement: string;\n};\n\nconst traverse = (\n typeof _traverse === 'function' ? _traverse : (_traverse as any).default\n) as typeof _traverse;\n\n/**\n * Processes a TSX/JSX/TS/JS file to extract content and inject Intlayer hooks/calls.\n */\nexport const processTsxFile = (\n filePath: string,\n componentKey: string,\n packageName: PackageName,\n configuration: IntlayerConfig,\n save: boolean = true,\n unmergedDictionaries: Record<string, unknown> = {},\n providedFileCode?: string\n): {\n extractedContent: Record<string, Record<string, string>>;\n modifiedCode: string;\n} | null => {\n const fileCode = providedFileCode ?? readFileSync(filePath, 'utf-8');\n\n const ast = parse(fileCode, {\n sourceType: 'module',\n plugins: ['jsx', 'typescript'],\n });\n\n const hasUseClient = ast.program.directives.some(\n (directive) => directive.value.value === 'use client'\n );\n\n const effectivePackageName =\n SERVER_CAPABLE_PACKAGES.has(packageName) && !hasUseClient\n ? `${packageName}/server`\n : packageName;\n\n // Both Solid and Angular expose content via a reactive/signal function —\n // access is `content().key` rather than `content.key`.\n const isSolid =\n packageName === 'solid-intlayer' || packageName === 'angular-intlayer';\n const existingKeys = new Set<string>();\n\n const {\n extractedContent,\n replacements,\n componentsNeedingHooks,\n componentKeyMap,\n componentPaths,\n hookMap,\n } = extractBabelContentForComponents(\n ast,\n fileCode,\n existingKeys,\n componentKey,\n configuration,\n filePath,\n unmergedDictionaries\n );\n\n if (Object.keys(extractedContent).length === 0) return null;\n\n const textEdits: TextEdit[] = [];\n\n // Build a lookup map: component AST node → NodePath (O(1) access)\n const componentNodeToPath = new Map<t.Node, NodePath>();\n for (const componentPath of componentPaths) {\n componentNodeToPath.set(componentPath.node, componentPath);\n }\n\n // Cache getExistingIntlayerInfo results for all component paths (avoids repeated traversals)\n const existingInfoCache = new Map<t.Node, ExistingIntlayerInfo | undefined>();\n for (const componentPath of componentPaths) {\n existingInfoCache.set(\n componentPath.node,\n getExistingIntlayerInfo(componentPath)\n );\n }\n\n /**\n * Walks up the ancestor chain to find the nearest enclosing component's\n * existing Intlayer info (if any).\n */\n const getExistingInfoForPath = (\n path: NodePath\n ): ExistingIntlayerInfo | undefined => {\n let current: NodePath | null = path;\n while (current) {\n if (componentNodeToPath.has(current.node)) {\n return existingInfoCache.get(current.node);\n }\n current = current.parentPath;\n }\n return undefined;\n };\n\n const getProvidingHookType = (\n path: NodePath\n ): 'useIntlayer' | 'getIntlayer' => {\n let current: NodePath | null = path;\n while (current) {\n const componentPath = componentNodeToPath.get(current.node);\n\n if (componentPath) {\n const existingInfo = existingInfoCache.get(componentPath.node);\n\n if (existingInfo) {\n return existingInfo.hook;\n }\n\n if (componentsNeedingHooks.has(componentPath)) {\n return hookMap.get(componentPath.node) || 'useIntlayer';\n }\n }\n current = current.parentPath;\n }\n return 'useIntlayer';\n };\n\n for (const {\n path,\n key,\n type,\n variables,\n childrenToReplace,\n } of replacements) {\n const existingInfo = getExistingInfoForPath(path);\n // When the existing call is destructured (e.g. `const { a } = getIntlayer(...)`),\n // new keys are added directly to that destructuring, so access them by name alone.\n const varName = existingInfo?.variableName ?? 'content';\n const contentAccessCode = existingInfo?.isDestructured\n ? key\n : isSolid\n ? `${varName}().${key}`\n : `${varName}.${key}`;\n const hookType = getProvidingHookType(path);\n const valueSuffix = hookType === 'getIntlayer' ? '' : '.value';\n\n if (type === 'jsx-text' && path.isJSXText()) {\n textEdits.push({\n start: path.node.start!,\n end: path.node.end!,\n replacement: `{${contentAccessCode}}`,\n });\n } else if (type === 'jsx-attribute' && path.isJSXAttribute()) {\n const valNode = path.node.value;\n\n if (valNode) {\n textEdits.push({\n start: valNode.start!,\n end: valNode.end!,\n replacement: `{${contentAccessCode}${valueSuffix}}`,\n });\n }\n } else if (type === 'string-literal' && path.isStringLiteral()) {\n textEdits.push({\n start: path.node.start!,\n end: path.node.end!,\n replacement: `${contentAccessCode}${valueSuffix}`,\n });\n } else if (\n type === 'jsx-text-combined' &&\n childrenToReplace &&\n childrenToReplace.length > 0\n ) {\n const accessStr = `{${contentAccessCode}}`;\n const start = childrenToReplace[0].start!;\n const end = childrenToReplace[childrenToReplace.length - 1].end!;\n textEdits.push({ start, end, replacement: accessStr });\n } else if (\n type === 'jsx-insertion' &&\n variables &&\n childrenToReplace &&\n childrenToReplace.length > 0\n ) {\n const objProps = variables\n .map((variableString) => {\n const [key, variableValue] = variableString\n .split(':')\n .map((part) => part.trim());\n return `${key}: ${variableValue}`;\n })\n .join(', ');\n\n const accessStr = `{${contentAccessCode}({ ${objProps} })}`;\n const start = childrenToReplace[0].start!;\n const end = childrenToReplace[childrenToReplace.length - 1].end!;\n textEdits.push({ start, end, replacement: accessStr });\n } else if (type === 'template-literal' && path.isTemplateLiteral()) {\n let replacement = `${contentAccessCode}`;\n\n if (variables && variables.length > 0) {\n const objProps = variables\n .map((variableString) => {\n const [key, variableValue] = variableString\n .split(':')\n .map((part) => part.trim());\n return `${key}: ${variableValue}`;\n })\n .join(', ');\n replacement += `({ ${objProps} })`;\n } else {\n replacement += valueSuffix;\n }\n\n textEdits.push({\n start: path.node.start!,\n end: path.node.end!,\n replacement,\n });\n }\n }\n\n let needsUseIntlayer = false;\n let needsGetIntlayer = false;\n\n for (const componentPath of componentsNeedingHooks) {\n const finalKey = componentKeyMap.get(componentPath.node)!;\n const existingInfo = existingInfoCache.get(componentPath.node);\n\n if (existingInfo) {\n if (existingInfo.hook === 'useIntlayer') needsUseIntlayer = true;\n\n if (existingInfo.hook === 'getIntlayer') needsGetIntlayer = true;\n\n // When the existing call is destructured, inject any missing keys into\n // the destructuring pattern so they can be accessed by name directly.\n if (existingInfo.isDestructured && existingInfo.objectPatternNode) {\n const neededKeys = new Set<string>();\n\n for (const { path: rPath, key: rKey } of replacements) {\n let current: NodePath | null = rPath;\n\n while (current) {\n if (current.node === componentPath.node) {\n neededKeys.add(rKey);\n break;\n }\n current = current.parentPath;\n }\n }\n\n const missingKeys = [...neededKeys].filter(\n (key) => !existingInfo.existingDestructuredKeys.includes(key)\n );\n\n if (missingKeys.length > 0) {\n const { objectPatternNode } = existingInfo;\n // Insert right after the last property so the space/newline before\n // `}` is naturally preserved: `{ a }` → `{ a, b }`.\n const lastProp =\n objectPatternNode.properties[\n objectPatternNode.properties.length - 1\n ];\n textEdits.push({\n start: lastProp.end!,\n end: lastProp.end!,\n replacement: `, ${missingKeys.join(', ')}`,\n });\n }\n }\n } else {\n const hook = hookMap.get(componentPath.node) || 'useIntlayer';\n\n if (hook === 'useIntlayer') needsUseIntlayer = true;\n\n if (hook === 'getIntlayer') needsGetIntlayer = true;\n\n const hookStatementStr = `\\n const content = ${hook}('${finalKey}');\\n`;\n\n if (componentPath.isProgram()) {\n // Find the last import or directive to inject the getIntlayer call\n let insertPos = 0;\n if (componentPath.node.directives.length > 0) {\n insertPos =\n componentPath.node.directives[\n componentPath.node.directives.length - 1\n ].end!;\n }\n for (const stmt of componentPath.node.body) {\n if (t.isImportDeclaration(stmt)) {\n insertPos = Math.max(insertPos, stmt.end!);\n }\n }\n\n textEdits.push({\n start: insertPos,\n end: insertPos,\n replacement: `\\n${hookStatementStr}`,\n });\n continue;\n }\n\n const bodyPath = componentPath.get('body') as NodePath;\n\n if (bodyPath.isBlockStatement()) {\n textEdits.push({\n start: bodyPath.node.start! + 1,\n end: bodyPath.node.start! + 1,\n replacement: hookStatementStr,\n });\n } else if (bodyPath.isExpression()) {\n const start = bodyPath.node.start!;\n const end = bodyPath.node.end!;\n\n // Detect wrapping parentheses: `() => (expr)` — scan backward from\n // the expression start and forward from its end for matching `(` / `)`.\n let parenStart = -1;\n let parenEnd = -1;\n\n for (let i = start - 1; i >= 0; i--) {\n const char = fileCode[i];\n if (char === '(') {\n parenStart = i;\n break;\n }\n if (char !== ' ' && char !== '\\n' && char !== '\\r' && char !== '\\t')\n break;\n }\n\n if (parenStart !== -1) {\n for (let i = end; i < fileCode.length; i++) {\n const char = fileCode[i];\n if (char === ')') {\n parenEnd = i;\n break;\n }\n if (char !== ' ' && char !== '\\n' && char !== '\\r' && char !== '\\t')\n break;\n }\n }\n\n if (parenStart !== -1 && parenEnd !== -1) {\n // Replace the surrounding `(` … `)` with a block statement. We keep\n // the parentheses as `return (…)` to prevent automatic semicolon\n // insertion when the returned expression starts on a new line.\n textEdits.push({\n start: parenEnd,\n end: parenEnd + 1,\n replacement: `)\\n}`,\n });\n textEdits.push({\n start: parenStart,\n end: parenStart + 1,\n replacement: `{\\n const content = ${hook}('${finalKey}');\\n return (`,\n });\n } else {\n textEdits.push({\n start: end,\n end: end,\n replacement: `\\n}`,\n });\n textEdits.push({\n start: start,\n end: start,\n replacement: `{\\n const content = ${hook}('${finalKey}');\\n return `,\n });\n }\n }\n }\n }\n\n const injectImport = (hookName: string, targetPackage: string) => {\n let existingImportPath: NodePath<t.ImportDeclaration> | undefined;\n\n traverse(ast, {\n ImportDeclaration(path) {\n if (path.node.source.value === targetPackage) {\n existingImportPath = path;\n path.stop();\n }\n },\n });\n\n if (!existingImportPath) {\n const newImportStr = `import { ${hookName} } from '${targetPackage}';\\n`;\n let insertPos = 0;\n\n if (ast.program.body.length > 0) {\n insertPos = ast.program.body[0].start!;\n } else if (ast.program.directives && ast.program.directives.length > 0) {\n insertPos =\n ast.program.directives[ast.program.directives.length - 1].end!;\n\n if (fileCode[insertPos] === ';') insertPos++;\n\n textEdits.push({\n start: insertPos,\n end: insertPos,\n replacement: `\\n${newImportStr}`,\n });\n return;\n }\n\n if (\n insertPos === 0 ||\n (ast.program.body.length > 0 &&\n insertPos === ast.program.body[0].start!)\n ) {\n textEdits.push({\n start: insertPos,\n end: insertPos,\n replacement: newImportStr,\n });\n }\n } else {\n const existingSpecifiers = existingImportPath.node.specifiers;\n const missingImport = !existingSpecifiers.some(\n (specifier) =>\n t.isImportSpecifier(specifier) &&\n t.isIdentifier(specifier.imported) &&\n specifier.imported.name === hookName\n );\n\n if (missingImport) {\n const importCode = fileCode.slice(\n existingImportPath.node.start!,\n existingImportPath.node.end!\n );\n const closingBraceIndex = importCode.lastIndexOf('}');\n\n if (closingBraceIndex !== -1) {\n const isCommaNeeded = !importCode\n .slice(0, closingBraceIndex)\n .trim()\n .endsWith(',');\n const absolutePos =\n existingImportPath.node.start! + closingBraceIndex;\n const prefix = isCommaNeeded ? ', ' : ' ';\n textEdits.push({\n start: absolutePos,\n end: absolutePos,\n replacement: `${prefix}${hookName} `,\n });\n }\n }\n }\n };\n\n if (needsUseIntlayer) injectImport('useIntlayer', effectivePackageName);\n\n if (needsGetIntlayer) injectImport('getIntlayer', 'intlayer');\n\n textEdits.sort((editA, editB) => {\n if (editB.start !== editA.start) return editB.start - editA.start;\n\n return editB.end - editA.end;\n });\n\n let formattedCode = fileCode;\n\n for (const edit of textEdits) {\n formattedCode =\n formattedCode.slice(0, edit.start) +\n edit.replacement +\n formattedCode.slice(edit.end);\n }\n\n if (save) {\n writeFileSync(filePath, formattedCode);\n\n const formatCommand = detectFormatCommand(configuration);\n\n if (formatCommand) {\n try {\n execSync(formatCommand.replace('{{file}}', filePath), {\n stdio: 'inherit',\n cwd: configuration.system.baseDir,\n });\n } catch (error) {\n console.error(error);\n }\n }\n }\n\n return { extractedContent, modifiedCode: formattedCode };\n};\n"],"mappings":"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"}
@@ -2,6 +2,7 @@
2
2
  const generateKey = (text, existingKeys) => {
3
3
  let key = text.normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[\s_-]+/g, " ").replace(/[^\p{L}\p{N} ]/gu, "").trim().split(" ").filter(Boolean).slice(0, 5).map((word, index) => index === 0 ? word.toLowerCase() : word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
4
4
  if (!key) key = "content";
5
+ if (/^[0-9]/.test(key)) key = `x${key}`;
5
6
  if (existingKeys.has(key)) {
6
7
  let i = 1;
7
8
  while (existingKeys.has(`${key}${i}`)) i++;
@@ -1 +1 @@
1
- {"version":3,"file":"generateKey.mjs","names":[],"sources":["../../../../src/extractContent/utils/generateKey.ts"],"sourcesContent":["export const generateKey = (\n text: string,\n existingKeys: Set<string>\n): string => {\n const maxWords = 5;\n let key = text\n .normalize('NFD') // Normalize to decomposes combined characters (e.g., é -> e + ´)\n .replace(/[\\u0300-\\u036f]/g, '') // Remove the accent characters\n .replace(/[\\s_-]+/g, ' ')\n .replace(/[^\\p{L}\\p{N} ]/gu, '')\n .trim()\n .split(' ')\n .filter(Boolean)\n .slice(0, maxWords)\n .map((word, index) =>\n index === 0\n ? word.toLowerCase()\n : word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()\n )\n .join('');\n\n if (!key) key = 'content';\n if (existingKeys.has(key)) {\n let i = 1;\n while (existingKeys.has(`${key}${i}`)) i++;\n key = `${key}${i}`;\n }\n return key;\n};\n"],"mappings":";AAAA,MAAa,eACX,MACA,iBACW;CAEX,IAAI,MAAM,KACP,UAAU,MAAM,CAChB,QAAQ,oBAAoB,GAAG,CAC/B,QAAQ,YAAY,IAAI,CACxB,QAAQ,oBAAoB,GAAG,CAC/B,MAAM,CACN,MAAM,IAAI,CACV,OAAO,QAAQ,CACf,MAAM,GATQ,EASI,CAClB,KAAK,MAAM,UACV,UAAU,IACN,KAAK,aAAa,GAClB,KAAK,OAAO,EAAE,CAAC,aAAa,GAAG,KAAK,MAAM,EAAE,CAAC,aAAa,CAC/D,CACA,KAAK,GAAG;AAEX,KAAI,CAAC,IAAK,OAAM;AAChB,KAAI,aAAa,IAAI,IAAI,EAAE;EACzB,IAAI,IAAI;AACR,SAAO,aAAa,IAAI,GAAG,MAAM,IAAI,CAAE;AACvC,QAAM,GAAG,MAAM;;AAEjB,QAAO"}
1
+ {"version":3,"file":"generateKey.mjs","names":[],"sources":["../../../../src/extractContent/utils/generateKey.ts"],"sourcesContent":["export const generateKey = (\n text: string,\n existingKeys: Set<string>\n): string => {\n const maxWords = 5;\n let key = text\n .normalize('NFD') // Normalize to decomposes combined characters (e.g., é -> e + ´)\n .replace(/[\\u0300-\\u036f]/g, '') // Remove the accent characters\n .replace(/[\\s_-]+/g, ' ')\n .replace(/[^\\p{L}\\p{N} ]/gu, '')\n .trim()\n .split(' ')\n .filter(Boolean)\n .slice(0, maxWords)\n .map((word, index) =>\n index === 0\n ? word.toLowerCase()\n : word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()\n )\n .join('');\n\n if (!key) key = 'content';\n\n // If the key starts with a number, prepend 'x' to make it a valid JS identifier\n if (/^[0-9]/.test(key)) {\n key = `x${key}`;\n }\n\n if (existingKeys.has(key)) {\n let i = 1;\n while (existingKeys.has(`${key}${i}`)) i++;\n key = `${key}${i}`;\n }\n return key;\n};\n"],"mappings":";AAAA,MAAa,eACX,MACA,iBACW;CAEX,IAAI,MAAM,KACP,UAAU,MAAM,CAChB,QAAQ,oBAAoB,GAAG,CAC/B,QAAQ,YAAY,IAAI,CACxB,QAAQ,oBAAoB,GAAG,CAC/B,MAAM,CACN,MAAM,IAAI,CACV,OAAO,QAAQ,CACf,MAAM,GATQ,EASI,CAClB,KAAK,MAAM,UACV,UAAU,IACN,KAAK,aAAa,GAClB,KAAK,OAAO,EAAE,CAAC,aAAa,GAAG,KAAK,MAAM,EAAE,CAAC,aAAa,CAC/D,CACA,KAAK,GAAG;AAEX,KAAI,CAAC,IAAK,OAAM;AAGhB,KAAI,SAAS,KAAK,IAAI,CACpB,OAAM,IAAI;AAGZ,KAAI,aAAa,IAAI,IAAI,EAAE;EACzB,IAAI,IAAI;AACR,SAAO,aAAa,IAAI,GAAG,MAAM,IAAI,CAAE;AACvC,QAAM,GAAG,MAAM;;AAEjB,QAAO"}
@@ -4,16 +4,22 @@
4
4
  *
5
5
  * Filters out:
6
6
  * - Empty strings
7
- * - Single words (typically icons or technical terms)
8
- * - Strings not starting with an uppercase letter (likely technical values)
7
+ * - Emails
8
+ * - Uncapitalized strings of 2 words or fewer (likely technical terms)
9
9
  * - Dynamic content patterns like Vue bindings (`v-`) or object patterns (`{`)
10
10
  */
11
11
  const shouldExtract = (text) => {
12
12
  const trimmed = text.trim();
13
13
  if (!trimmed) return false;
14
- if (!trimmed.includes(" ")) return false;
15
- if (!/^[A-Z]/.test(trimmed)) return false;
14
+ if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(trimmed)) return false;
16
15
  if (trimmed.startsWith("{") || trimmed.startsWith("v-")) return false;
16
+ const wordCount = trimmed.split(/\s+/).length;
17
+ const isCapitalized = /^[A-Z]/.test(trimmed);
18
+ if (wordCount === 1) {
19
+ if (/[a-z][A-Z]/.test(trimmed)) return false;
20
+ if (trimmed.includes("-") || trimmed.includes("_")) return false;
21
+ }
22
+ if (!isCapitalized && wordCount <= 2) return false;
17
23
  return true;
18
24
  };
19
25
 
@@ -1 +1 @@
1
- {"version":3,"file":"shouldExtract.mjs","names":[],"sources":["../../../../src/extractContent/utils/shouldExtract.ts"],"sourcesContent":["/**\n * Checks whether the given text should be extracted as a translatable string.\n *\n * Filters out:\n * - Empty strings\n * - Single words (typically icons or technical terms)\n * - Strings not starting with an uppercase letter (likely technical values)\n * - Dynamic content patterns like Vue bindings (`v-`) or object patterns (`{`)\n */\nexport const shouldExtract = (text: string): boolean => {\n const trimmed = text.trim();\n\n if (!trimmed) return false;\n\n // We usually want to extract full sentences or labels, not single technical words\n if (!trimmed.includes(' ')) return false;\n\n // We assume content to extract starts with an uppercase letter\n if (!/^[A-Z]/.test(trimmed)) return false;\n\n // Ignore dynamic content patterns\n if (trimmed.startsWith('{') || trimmed.startsWith('v-')) return false;\n\n return true;\n};\n"],"mappings":";;;;;;;;;;AASA,MAAa,iBAAiB,SAA0B;CACtD,MAAM,UAAU,KAAK,MAAM;AAE3B,KAAI,CAAC,QAAS,QAAO;AAGrB,KAAI,CAAC,QAAQ,SAAS,IAAI,CAAE,QAAO;AAGnC,KAAI,CAAC,SAAS,KAAK,QAAQ,CAAE,QAAO;AAGpC,KAAI,QAAQ,WAAW,IAAI,IAAI,QAAQ,WAAW,KAAK,CAAE,QAAO;AAEhE,QAAO"}
1
+ {"version":3,"file":"shouldExtract.mjs","names":[],"sources":["../../../../src/extractContent/utils/shouldExtract.ts"],"sourcesContent":["/**\n * Checks whether the given text should be extracted as a translatable string.\n *\n * Filters out:\n * - Empty strings\n * - Emails\n * - Uncapitalized strings of 2 words or fewer (likely technical terms)\n * - Dynamic content patterns like Vue bindings (`v-`) or object patterns (`{`)\n */\nexport const shouldExtract = (text: string): boolean => {\n const trimmed = text.trim();\n\n if (!trimmed) return false;\n\n // Ignore emails\n if (/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(trimmed)) return false;\n\n // Ignore dynamic content patterns\n if (trimmed.startsWith('{') || trimmed.startsWith('v-')) return false;\n\n const wordCount = trimmed.split(/\\s+/).length;\n const isCapitalized = /^[A-Z]/.test(trimmed);\n\n // Ignore technical identifiers (one word strings with camelCase, kebab-case, snake_case etc.)\n if (wordCount === 1) {\n // CamelCase or internal capitals (like camelCaseProperty or CamelCaseProperty)\n if (/[a-z][A-Z]/.test(trimmed)) return false;\n // kebab-case or snake_case\n if (trimmed.includes('-') || trimmed.includes('_')) return false;\n }\n\n // We usually want to extract full sentences or labels, not single/short technical words.\n // Extract if capitalized, or if it contains more than 2 words.\n if (!isCapitalized && wordCount <= 2) return false;\n\n return true;\n};\n"],"mappings":";;;;;;;;;;AASA,MAAa,iBAAiB,SAA0B;CACtD,MAAM,UAAU,KAAK,MAAM;AAE3B,KAAI,CAAC,QAAS,QAAO;AAGrB,KAAI,6BAA6B,KAAK,QAAQ,CAAE,QAAO;AAGvD,KAAI,QAAQ,WAAW,IAAI,IAAI,QAAQ,WAAW,KAAK,CAAE,QAAO;CAEhE,MAAM,YAAY,QAAQ,MAAM,MAAM,CAAC;CACvC,MAAM,gBAAgB,SAAS,KAAK,QAAQ;AAG5C,KAAI,cAAc,GAAG;AAEnB,MAAI,aAAa,KAAK,QAAQ,CAAE,QAAO;AAEvC,MAAI,QAAQ,SAAS,IAAI,IAAI,QAAQ,SAAS,IAAI,CAAE,QAAO;;AAK7D,KAAI,CAAC,iBAAiB,aAAa,EAAG,QAAO;AAE7C,QAAO"}
@@ -6,7 +6,7 @@ import { NodePath } from "@babel/traverse";
6
6
  type BabelReplacement = {
7
7
  path: NodePath;
8
8
  key: string;
9
- type: 'jsx-text' | 'jsx-attribute' | 'string-literal' | 'jsx-insertion' | 'jsx-text-combined';
9
+ type: 'jsx-text' | 'jsx-attribute' | 'string-literal' | 'jsx-insertion' | 'jsx-text-combined' | 'template-literal';
10
10
  componentKey: string;
11
11
  childrenToReplace?: t.Node[];
12
12
  variables?: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"babelProcessor.d.ts","names":[],"sources":["../../../src/extractContent/babelProcessor.ts"],"mappings":";;;;;KAYY,gBAAA;EACV,IAAA,EAAM,QAAA;EACN,GAAA;EACA,IAAA;EAMA,YAAA;EACA,iBAAA,GAAoB,CAAA,CAAE,IAAA;EACtB,SAAA;AAAA;;;;;cAYW,uBAAA,GACX,IAAA,EAAM,QAAA,CAAS,CAAA,CAAE,UAAA,GAAa,CAAA,CAAE,WAAA,GAChC,QAAA,UACA,YAAA,EAAc,GAAA,UACd,sBAAA,GAAyB,IAAA,EAAM,QAAA,aAC/B,gBAAA,EAAkB,MAAA,SAAe,MAAA,mBACjC,YAAA,EAAc,gBAAA,IACd,YAAA,EAAc,GAAA,CAAI,CAAA,CAAE,IAAA;;;;;cA6GT,gCAAA,GACX,GAAA,EAAK,CAAA,CAAE,IAAA,EACP,QAAA,UACA,YAAA,EAAc,GAAA,UACd,UAAA,UACA,aAAA,EAAe,cAAA,EACf,QAAA,UACA,oBAAA,GAAsB,MAAA;EAEtB,gBAAA,EAAkB,MAAA,SAAe,MAAA;EACjC,YAAA,EAAc,gBAAA;EACd,sBAAA,EAAwB,GAAA,CAAI,QAAA;EAC5B,eAAA,EAAiB,GAAA,CAAI,CAAA,CAAE,IAAA;EACvB,cAAA,EAAgB,QAAA;EAChB,OAAA,EAAS,GAAA,CAAI,CAAA,CAAE,IAAA;EACf,OAAA;AAAA;;;;cAgPW,gBAAA,GACX,GAAA,EAAK,CAAA,CAAE,IAAA,EACP,QAAA,UACA,YAAA,EAAc,GAAA,UACd,aAAA,EAAe,cAAA,EACf,QAAA,UACA,oBAAA,GAAsB,MAAA;EAEtB,gBAAA,EAAkB,MAAA;EAClB,YAAA,EAAc,gBAAA;AAAA"}
1
+ {"version":3,"file":"babelProcessor.d.ts","names":[],"sources":["../../../src/extractContent/babelProcessor.ts"],"mappings":";;;;;KAYY,gBAAA;EACV,IAAA,EAAM,QAAA;EACN,GAAA;EACA,IAAA;EAOA,YAAA;EACA,iBAAA,GAAoB,CAAA,CAAE,IAAA;EACtB,SAAA;AAAA;;;;;cAYW,uBAAA,GACX,IAAA,EAAM,QAAA,CAAS,CAAA,CAAE,UAAA,GAAa,CAAA,CAAE,WAAA,GAChC,QAAA,UACA,YAAA,EAAc,GAAA,UACd,sBAAA,GAAyB,IAAA,EAAM,QAAA,aAC/B,gBAAA,EAAkB,MAAA,SAAe,MAAA,mBACjC,YAAA,EAAc,gBAAA,IACd,YAAA,EAAc,GAAA,CAAI,CAAA,CAAE,IAAA;;;;;cAqIT,gCAAA,GACX,GAAA,EAAK,CAAA,CAAE,IAAA,EACP,QAAA,UACA,YAAA,EAAc,GAAA,UACd,UAAA,UACA,aAAA,EAAe,cAAA,EACf,QAAA,UACA,oBAAA,GAAsB,MAAA;EAEtB,gBAAA,EAAkB,MAAA,SAAe,MAAA;EACjC,YAAA,EAAc,gBAAA;EACd,sBAAA,EAAwB,GAAA,CAAI,QAAA;EAC5B,eAAA,EAAiB,GAAA,CAAI,CAAA,CAAE,IAAA;EACvB,cAAA,EAAgB,QAAA;EAChB,OAAA,EAAS,GAAA,CAAI,CAAA,CAAE,IAAA;EACf,OAAA;AAAA;;;;cAwVW,gBAAA,GACX,GAAA,EAAK,CAAA,CAAE,IAAA,EACP,QAAA,UACA,YAAA,EAAc,GAAA,UACd,aAAA,EAAe,cAAA,EACf,QAAA,UACA,oBAAA,GAAsB,MAAA;EAEtB,gBAAA,EAAkB,MAAA;EAClB,YAAA,EAAc,gBAAA;AAAA"}
@@ -4,8 +4,8 @@
4
4
  *
5
5
  * Filters out:
6
6
  * - Empty strings
7
- * - Single words (typically icons or technical terms)
8
- * - Strings not starting with an uppercase letter (likely technical values)
7
+ * - Emails
8
+ * - Uncapitalized strings of 2 words or fewer (likely technical terms)
9
9
  * - Dynamic content patterns like Vue bindings (`v-`) or object patterns (`{`)
10
10
  */
11
11
  declare const shouldExtract: (text: string) => boolean;