@intlayer/babel 8.10.1 → 8.11.0-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,6 +13,21 @@ let _babel_types = require("@babel/types");
13
13
  _babel_types = require_runtime.__toESM(_babel_types);
14
14
 
15
15
  //#region src/extractContent/processTsxFile.ts
16
+ /**
17
+ * Returns true when a string literal sits inside a JSX child expression
18
+ * container (i.e. a React node slot), meaning content.key renders as an
19
+ * IntlayerNode and should NOT receive a .value suffix.
20
+ * Returns false for attribute-value containers and non-JSX contexts.
21
+ */
22
+ const isInJsxNodeContext = (path) => {
23
+ let current = path.parentPath;
24
+ while (current) {
25
+ if (current.isJSXExpressionContainer()) return !current.parentPath?.isJSXAttribute();
26
+ if (current.isFunction() || current.isProgram()) return false;
27
+ current = current.parentPath;
28
+ }
29
+ return false;
30
+ };
16
31
  const traverse = typeof _babel_traverse.default === "function" ? _babel_traverse.default : _babel_traverse.default.default;
17
32
  /**
18
33
  * Processes a TSX/JSX/TS/JS file to extract content and inject Intlayer hooks/calls.
@@ -102,12 +117,14 @@ const processTsxFile = (filePath, componentKey, packageName, configuration, save
102
117
  end: valNode.end,
103
118
  replacement: `{${contentAccessCode}${valueSuffix}}`
104
119
  });
105
- } else if (type === "string-literal" && path.isStringLiteral()) textEdits.push({
106
- start: path.node.start,
107
- end: path.node.end,
108
- replacement: `${contentAccessCode}${valueSuffix}`
109
- });
110
- else if (type === "jsx-text-combined" && childrenToReplace && childrenToReplace.length > 0) {
120
+ } else if (type === "string-literal" && path.isStringLiteral()) {
121
+ const suffix = isInJsxNodeContext(path) ? "" : valueSuffix;
122
+ textEdits.push({
123
+ start: path.node.start,
124
+ end: path.node.end,
125
+ replacement: `${contentAccessCode}${suffix}`
126
+ });
127
+ } else if (type === "jsx-text-combined" && childrenToReplace && childrenToReplace.length > 0) {
111
128
  const accessStr = `{${contentAccessCode}}`;
112
129
  const start = childrenToReplace[0].start;
113
130
  const end = childrenToReplace[childrenToReplace.length - 1].end;
@@ -1 +1 @@
1
- {"version":3,"file":"processTsxFile.cjs","names":["_traverse","SERVER_CAPABLE_PACKAGES","extractBabelContentForComponents","getExistingIntlayerInfo","t"],"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 // Angular exposes content via a reactive signal — access is `content().key`.\n // solid-intlayer now returns a Proxy that supports direct `content.key` access.\n const usesSignalAccessor = 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 const componentPath = componentNodeToPath.get(current.node)!;\n\n const existingInfo = existingInfoCache.get(current.node);\n if (existingInfo) {\n return existingInfo;\n }\n\n if (componentsNeedingHooks.has(componentPath)) {\n return undefined;\n }\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 const generatedVarNames = new Map<NodePath, string>();\n for (const componentPath of componentsNeedingHooks) {\n let varName = 'content';\n let counter = 1;\n while (componentPath.scope.hasBinding(varName)) {\n varName = `content${counter}`;\n counter++;\n }\n generatedVarNames.set(componentPath, varName);\n }\n\n const getProvidingVarName = (path: NodePath): string => {\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 if (existingInfo) {\n return existingInfo.variableName ?? 'content';\n }\n if (componentsNeedingHooks.has(componentPath)) {\n return generatedVarNames.get(componentPath) || 'content';\n }\n }\n current = current.parentPath;\n }\n return 'content';\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 ?? getProvidingVarName(path);\n const contentAccessCode = existingInfo?.isDestructured\n ? key\n : usesSignalAccessor\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 hookVarName = generatedVarNames.get(componentPath) || 'content';\n const hookStatementStr = `\\n const ${hookVarName} = ${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 if (insertPos === 0 && componentPath.node.body.length > 0) {\n insertPos = componentPath.node.body[0].start!;\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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"}
1
+ {"version":3,"file":"processTsxFile.cjs","names":["_traverse","SERVER_CAPABLE_PACKAGES","extractBabelContentForComponents","getExistingIntlayerInfo","t"],"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\n/**\n * Returns true when a string literal sits inside a JSX child expression\n * container (i.e. a React node slot), meaning content.key renders as an\n * IntlayerNode and should NOT receive a .value suffix.\n * Returns false for attribute-value containers and non-JSX contexts.\n */\nconst isInJsxNodeContext = (path: NodePath): boolean => {\n let current: NodePath | null = path.parentPath;\n while (current) {\n if (current.isJSXExpressionContainer()) {\n return !current.parentPath?.isJSXAttribute();\n }\n if (current.isFunction() || current.isProgram()) return false;\n current = current.parentPath;\n }\n return false;\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 // Angular exposes content via a reactive signal — access is `content().key`.\n // solid-intlayer now returns a Proxy that supports direct `content.key` access.\n const usesSignalAccessor = 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 const componentPath = componentNodeToPath.get(current.node)!;\n\n const existingInfo = existingInfoCache.get(current.node);\n if (existingInfo) {\n return existingInfo;\n }\n\n if (componentsNeedingHooks.has(componentPath)) {\n return undefined;\n }\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 const generatedVarNames = new Map<NodePath, string>();\n for (const componentPath of componentsNeedingHooks) {\n let varName = 'content';\n let counter = 1;\n while (componentPath.scope.hasBinding(varName)) {\n varName = `content${counter}`;\n counter++;\n }\n generatedVarNames.set(componentPath, varName);\n }\n\n const getProvidingVarName = (path: NodePath): string => {\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 if (existingInfo) {\n return existingInfo.variableName ?? 'content';\n }\n if (componentsNeedingHooks.has(componentPath)) {\n return generatedVarNames.get(componentPath) || 'content';\n }\n }\n current = current.parentPath;\n }\n return 'content';\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 ?? getProvidingVarName(path);\n const contentAccessCode = existingInfo?.isDestructured\n ? key\n : usesSignalAccessor\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 const suffix = isInJsxNodeContext(path) ? '' : valueSuffix;\n textEdits.push({\n start: path.node.start!,\n end: path.node.end!,\n replacement: `${contentAccessCode}${suffix}`,\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 hookVarName = generatedVarNames.get(componentPath) || 'content';\n const hookStatementStr = `\\n const ${hookVarName} = ${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 if (insertPos === 0 && componentPath.node.body.length > 0) {\n insertPos = componentPath.node.body[0].start!;\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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"}
@@ -14,7 +14,7 @@ const shouldExtract = (text) => {
14
14
  const trimmed = text.trim();
15
15
  if (!trimmed) return false;
16
16
  if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(trimmed)) return false;
17
- if (trimmed.startsWith("{") || trimmed.startsWith("v-")) return false;
17
+ if (trimmed.startsWith("{") && !trimmed.startsWith("{{") || trimmed.startsWith("v-")) return false;
18
18
  if (trimmed.includes("=>") || trimmed.includes(");") || trimmed.includes("(){") || trimmed.includes("==") || trimmed.includes("window.") || trimmed.startsWith("(function") || trimmed.startsWith("function(")) return false;
19
19
  if ((trimmed.match(/[^\p{L}\p{N}\s.,!?;:'"()[\]{}–—/«»„“\p{Sc}%&*+#@^_+=<>/~]/gu) || []).length > 5) return false;
20
20
  const wordCount = trimmed.split(/\s+/).length;
@@ -1 +1 @@
1
- {"version":3,"file":"shouldExtract.cjs","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 // Ignore explicit code patterns (markers)\n if (\n trimmed.includes('=>') ||\n trimmed.includes(');') ||\n trimmed.includes('(){') ||\n trimmed.includes('==') ||\n trimmed.includes('window.') ||\n trimmed.startsWith('(function') ||\n trimmed.startsWith('function(')\n ) {\n return false;\n }\n\n // Heuristic: check for characters that are common in code but rare in natural text.\n // Whitelist: letters, numbers, spaces, and frequent text symbols (including punctuation, braces, and technical symbols)\n const nonTextualRegex =\n /[^\\p{L}\\p{N}\\s.,!?;:'\"()[\\]{}–—/«»„“\\p{Sc}%&*+#@^_+=<>/~]/gu;\n const nonTextualMatches = trimmed.match(nonTextualRegex) || [];\n\n // If a string contains a high density of truly exceptional symbols (like |, \\, etc.),\n // it is highly likely to be code or complex technical data.\n if (nonTextualMatches.length > 5) return false;\n\n const wordCount = trimmed.split(/\\s+/).length;\n\n // Ignore CSS/Tailwind utility class strings. A string whose tokens all look\n // like CSS utility classes (lowercase, optional responsive/state prefix like\n // \"sm:\" or \"hover:\", optional hyphenated suffix like \"-4\" or \"-full\") and\n // where at least one token contains a hyphen is almost certainly a className\n // value, not translatable text.\n const cssClassTokenRegex =\n /^!?([a-z][a-z0-9-]*:)*[a-z][a-z0-9]*(-[a-z0-9[\\].,%#/]+)*(\\/[a-z0-9]+)?$/;\n if (wordCount > 1) {\n const tokens = trimmed.split(/\\s+/);\n if (\n tokens.every((token) => cssClassTokenRegex.test(token)) &&\n tokens.some((token) => token.includes('-'))\n ) {\n return false;\n }\n }\n\n // Check if starts with a capital letter (including after an opening parenthesis/quote)\n const isCapitalized = /^['\"([]*\\p{Lu}/u.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]\\p{Lu}/u.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,KAAK;CAE1B,IAAI,CAAC,SAAS,OAAO;CAGrB,IAAI,6BAA6B,KAAK,OAAO,GAAG,OAAO;CAGvD,IAAI,QAAQ,WAAW,GAAG,KAAK,QAAQ,WAAW,IAAI,GAAG,OAAO;CAGhE,IACE,QAAQ,SAAS,IAAI,KACrB,QAAQ,SAAS,IAAI,KACrB,QAAQ,SAAS,KAAK,KACtB,QAAQ,SAAS,IAAI,KACrB,QAAQ,SAAS,SAAS,KAC1B,QAAQ,WAAW,WAAW,KAC9B,QAAQ,WAAW,WAAW,GAE9B,OAAO;CAWT,KAJ0B,QAAQ,MAAM,6DAAe,KAAK,CAAC,GAIvC,SAAS,GAAG,OAAO;CAEzC,MAAM,YAAY,QAAQ,MAAM,KAAK,EAAE;CAOvC,MAAM,qBACJ;CACF,IAAI,YAAY,GAAG;EACjB,MAAM,SAAS,QAAQ,MAAM,KAAK;EAClC,IACE,OAAO,OAAO,UAAU,mBAAmB,KAAK,KAAK,CAAC,KACtD,OAAO,MAAM,UAAU,MAAM,SAAS,GAAG,CAAC,GAE1C,OAAO;CAEX;CAGA,MAAM,gBAAgB,kBAAkB,KAAK,OAAO;CAGpD,IAAI,cAAc,GAAG;EAEnB,IAAI,eAAe,KAAK,OAAO,GAAG,OAAO;EAEzC,IAAI,QAAQ,SAAS,GAAG,KAAK,QAAQ,SAAS,GAAG,GAAG,OAAO;CAC7D;CAIA,IAAI,CAAC,iBAAiB,aAAa,GAAG,OAAO;CAE7C,OAAO;AACT"}
1
+ {"version":3,"file":"shouldExtract.cjs","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, but allow {{placeholder}} which is intlayer's insertion format\n if (\n (trimmed.startsWith('{') && !trimmed.startsWith('{{')) ||\n trimmed.startsWith('v-')\n )\n return false;\n\n // Ignore explicit code patterns (markers)\n if (\n trimmed.includes('=>') ||\n trimmed.includes(');') ||\n trimmed.includes('(){') ||\n trimmed.includes('==') ||\n trimmed.includes('window.') ||\n trimmed.startsWith('(function') ||\n trimmed.startsWith('function(')\n ) {\n return false;\n }\n\n // Heuristic: check for characters that are common in code but rare in natural text.\n // Whitelist: letters, numbers, spaces, and frequent text symbols (including punctuation, braces, and technical symbols)\n const nonTextualRegex =\n /[^\\p{L}\\p{N}\\s.,!?;:'\"()[\\]{}–—/«»„“\\p{Sc}%&*+#@^_+=<>/~]/gu;\n const nonTextualMatches = trimmed.match(nonTextualRegex) || [];\n\n // If a string contains a high density of truly exceptional symbols (like |, \\, etc.),\n // it is highly likely to be code or complex technical data.\n if (nonTextualMatches.length > 5) return false;\n\n const wordCount = trimmed.split(/\\s+/).length;\n\n // Ignore CSS/Tailwind utility class strings. A string whose tokens all look\n // like CSS utility classes (lowercase, optional responsive/state prefix like\n // \"sm:\" or \"hover:\", optional hyphenated suffix like \"-4\" or \"-full\") and\n // where at least one token contains a hyphen is almost certainly a className\n // value, not translatable text.\n const cssClassTokenRegex =\n /^!?([a-z][a-z0-9-]*:)*[a-z][a-z0-9]*(-[a-z0-9[\\].,%#/]+)*(\\/[a-z0-9]+)?$/;\n if (wordCount > 1) {\n const tokens = trimmed.split(/\\s+/);\n if (\n tokens.every((token) => cssClassTokenRegex.test(token)) &&\n tokens.some((token) => token.includes('-'))\n ) {\n return false;\n }\n }\n\n // Check if starts with a capital letter (including after an opening parenthesis/quote)\n const isCapitalized = /^['\"([]*\\p{Lu}/u.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]\\p{Lu}/u.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,KAAK;CAE1B,IAAI,CAAC,SAAS,OAAO;CAGrB,IAAI,6BAA6B,KAAK,OAAO,GAAG,OAAO;CAGvD,IACG,QAAQ,WAAW,GAAG,KAAK,CAAC,QAAQ,WAAW,IAAI,KACpD,QAAQ,WAAW,IAAI,GAEvB,OAAO;CAGT,IACE,QAAQ,SAAS,IAAI,KACrB,QAAQ,SAAS,IAAI,KACrB,QAAQ,SAAS,KAAK,KACtB,QAAQ,SAAS,IAAI,KACrB,QAAQ,SAAS,SAAS,KAC1B,QAAQ,WAAW,WAAW,KAC9B,QAAQ,WAAW,WAAW,GAE9B,OAAO;CAWT,KAJ0B,QAAQ,MAAM,6DAAe,KAAK,CAAC,GAIvC,SAAS,GAAG,OAAO;CAEzC,MAAM,YAAY,QAAQ,MAAM,KAAK,EAAE;CAOvC,MAAM,qBACJ;CACF,IAAI,YAAY,GAAG;EACjB,MAAM,SAAS,QAAQ,MAAM,KAAK;EAClC,IACE,OAAO,OAAO,UAAU,mBAAmB,KAAK,KAAK,CAAC,KACtD,OAAO,MAAM,UAAU,MAAM,SAAS,GAAG,CAAC,GAE1C,OAAO;CAEX;CAGA,MAAM,gBAAgB,kBAAkB,KAAK,OAAO;CAGpD,IAAI,cAAc,GAAG;EAEnB,IAAI,eAAe,KAAK,OAAO,GAAG,OAAO;EAEzC,IAAI,QAAQ,SAAS,GAAG,KAAK,QAAQ,SAAS,GAAG,GAAG,OAAO;CAC7D;CAIA,IAAI,CAAC,iBAAiB,aAAa,GAAG,OAAO;CAE7C,OAAO;AACT"}
@@ -9,6 +9,21 @@ import _traverse from "@babel/traverse";
9
9
  import * as t from "@babel/types";
10
10
 
11
11
  //#region src/extractContent/processTsxFile.ts
12
+ /**
13
+ * Returns true when a string literal sits inside a JSX child expression
14
+ * container (i.e. a React node slot), meaning content.key renders as an
15
+ * IntlayerNode and should NOT receive a .value suffix.
16
+ * Returns false for attribute-value containers and non-JSX contexts.
17
+ */
18
+ const isInJsxNodeContext = (path) => {
19
+ let current = path.parentPath;
20
+ while (current) {
21
+ if (current.isJSXExpressionContainer()) return !current.parentPath?.isJSXAttribute();
22
+ if (current.isFunction() || current.isProgram()) return false;
23
+ current = current.parentPath;
24
+ }
25
+ return false;
26
+ };
12
27
  const traverse = typeof _traverse === "function" ? _traverse : _traverse.default;
13
28
  /**
14
29
  * Processes a TSX/JSX/TS/JS file to extract content and inject Intlayer hooks/calls.
@@ -98,12 +113,14 @@ const processTsxFile = (filePath, componentKey, packageName, configuration, save
98
113
  end: valNode.end,
99
114
  replacement: `{${contentAccessCode}${valueSuffix}}`
100
115
  });
101
- } else if (type === "string-literal" && path.isStringLiteral()) textEdits.push({
102
- start: path.node.start,
103
- end: path.node.end,
104
- replacement: `${contentAccessCode}${valueSuffix}`
105
- });
106
- else if (type === "jsx-text-combined" && childrenToReplace && childrenToReplace.length > 0) {
116
+ } else if (type === "string-literal" && path.isStringLiteral()) {
117
+ const suffix = isInJsxNodeContext(path) ? "" : valueSuffix;
118
+ textEdits.push({
119
+ start: path.node.start,
120
+ end: path.node.end,
121
+ replacement: `${contentAccessCode}${suffix}`
122
+ });
123
+ } else if (type === "jsx-text-combined" && childrenToReplace && childrenToReplace.length > 0) {
107
124
  const accessStr = `{${contentAccessCode}}`;
108
125
  const start = childrenToReplace[0].start;
109
126
  const end = childrenToReplace[childrenToReplace.length - 1].end;
@@ -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 // Angular exposes content via a reactive signal — access is `content().key`.\n // solid-intlayer now returns a Proxy that supports direct `content.key` access.\n const usesSignalAccessor = 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 const componentPath = componentNodeToPath.get(current.node)!;\n\n const existingInfo = existingInfoCache.get(current.node);\n if (existingInfo) {\n return existingInfo;\n }\n\n if (componentsNeedingHooks.has(componentPath)) {\n return undefined;\n }\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 const generatedVarNames = new Map<NodePath, string>();\n for (const componentPath of componentsNeedingHooks) {\n let varName = 'content';\n let counter = 1;\n while (componentPath.scope.hasBinding(varName)) {\n varName = `content${counter}`;\n counter++;\n }\n generatedVarNames.set(componentPath, varName);\n }\n\n const getProvidingVarName = (path: NodePath): string => {\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 if (existingInfo) {\n return existingInfo.variableName ?? 'content';\n }\n if (componentsNeedingHooks.has(componentPath)) {\n return generatedVarNames.get(componentPath) || 'content';\n }\n }\n current = current.parentPath;\n }\n return 'content';\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 ?? getProvidingVarName(path);\n const contentAccessCode = existingInfo?.isDestructured\n ? key\n : usesSignalAccessor\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 hookVarName = generatedVarNames.get(componentPath) || 'content';\n const hookStatementStr = `\\n const ${hookVarName} = ${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 if (insertPos === 0 && componentPath.node.body.length > 0) {\n insertPos = componentPath.node.body[0].start!;\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":";;;;;;;;;;;AAqBA,MAAM,WACJ,OAAO,cAAc,aAAa,YAAa,UAAkB;;;;AAMnE,MAAa,kBACX,UACA,cACA,aACA,eACA,OAAgB,MAChB,uBAAgD,CAAC,GACjD,qBAIU;CACV,MAAM,WAAW,oBAAoB,aAAa,UAAU,OAAO;CAEnE,MAAM,MAAM,MAAM,UAAU;EAC1B,YAAY;EACZ,SAAS,CAAC,OAAO,YAAY;CAC/B,CAAC;CAED,MAAM,eAAe,IAAI,QAAQ,WAAW,MACzC,cAAc,UAAU,MAAM,UAAU,YAC3C;CAEA,MAAM,uBACJ,wBAAwB,IAAI,WAAW,KAAK,CAAC,eACzC,GAAG,YAAY,WACf;CAIN,MAAM,qBAAqB,gBAAgB;CAG3C,MAAM,EACJ,kBACA,cACA,wBACA,iBACA,gBACA,YACE,iCACF,KACA,0BACA,IAZuB,IAYZ,GACX,cACA,eACA,UACA,oBACF;CAEA,IAAI,OAAO,KAAK,gBAAgB,EAAE,WAAW,GAAG,OAAO;CAEvD,MAAM,YAAwB,CAAC;CAG/B,MAAM,sCAAsB,IAAI,IAAsB;CACtD,KAAK,MAAM,iBAAiB,gBAC1B,oBAAoB,IAAI,cAAc,MAAM,aAAa;CAI3D,MAAM,oCAAoB,IAAI,IAA8C;CAC5E,KAAK,MAAM,iBAAiB,gBAC1B,kBAAkB,IAChB,cAAc,MACd,wBAAwB,aAAa,CACvC;;;;;CAOF,MAAM,0BACJ,SACqC;EACrC,IAAI,UAA2B;EAC/B,OAAO,SAAS;GACd,IAAI,oBAAoB,IAAI,QAAQ,IAAI,GAAG;IACzC,MAAM,gBAAgB,oBAAoB,IAAI,QAAQ,IAAI;IAE1D,MAAM,eAAe,kBAAkB,IAAI,QAAQ,IAAI;IACvD,IAAI,cACF,OAAO;IAGT,IAAI,uBAAuB,IAAI,aAAa,GAC1C;GAEJ;GACA,UAAU,QAAQ;EACpB;CAEF;CAEA,MAAM,wBACJ,SACkC;EAClC,IAAI,UAA2B;EAC/B,OAAO,SAAS;GACd,MAAM,gBAAgB,oBAAoB,IAAI,QAAQ,IAAI;GAE1D,IAAI,eAAe;IACjB,MAAM,eAAe,kBAAkB,IAAI,cAAc,IAAI;IAE7D,IAAI,cACF,OAAO,aAAa;IAGtB,IAAI,uBAAuB,IAAI,aAAa,GAC1C,OAAO,QAAQ,IAAI,cAAc,IAAI,KAAK;GAE9C;GACA,UAAU,QAAQ;EACpB;EACA,OAAO;CACT;CAEA,MAAM,oCAAoB,IAAI,IAAsB;CACpD,KAAK,MAAM,iBAAiB,wBAAwB;EAClD,IAAI,UAAU;EACd,IAAI,UAAU;EACd,OAAO,cAAc,MAAM,WAAW,OAAO,GAAG;GAC9C,UAAU,UAAU;GACpB;EACF;EACA,kBAAkB,IAAI,eAAe,OAAO;CAC9C;CAEA,MAAM,uBAAuB,SAA2B;EACtD,IAAI,UAA2B;EAC/B,OAAO,SAAS;GACd,MAAM,gBAAgB,oBAAoB,IAAI,QAAQ,IAAI;GAE1D,IAAI,eAAe;IACjB,MAAM,eAAe,kBAAkB,IAAI,cAAc,IAAI;IAC7D,IAAI,cACF,OAAO,aAAa,gBAAgB;IAEtC,IAAI,uBAAuB,IAAI,aAAa,GAC1C,OAAO,kBAAkB,IAAI,aAAa,KAAK;GAEnD;GACA,UAAU,QAAQ;EACpB;EACA,OAAO;CACT;CAEA,KAAK,MAAM,EACT,MACA,KACA,MACA,WACA,uBACG,cAAc;EACjB,MAAM,eAAe,uBAAuB,IAAI;EAGhD,MAAM,UAAU,cAAc,gBAAgB,oBAAoB,IAAI;EACtE,MAAM,oBAAoB,cAAc,iBACpC,MACA,qBACE,GAAG,QAAQ,KAAK,QAChB,GAAG,QAAQ,GAAG;EAEpB,MAAM,cADW,qBAAqB,IACX,MAAM,gBAAgB,KAAK;EAEtD,IAAI,SAAS,cAAc,KAAK,UAAU,GACxC,UAAU,KAAK;GACb,OAAO,KAAK,KAAK;GACjB,KAAK,KAAK,KAAK;GACf,aAAa,IAAI,kBAAkB;EACrC,CAAC;OACI,IAAI,SAAS,mBAAmB,KAAK,eAAe,GAAG;GAC5D,MAAM,UAAU,KAAK,KAAK;GAE1B,IAAI,SACF,UAAU,KAAK;IACb,OAAO,QAAQ;IACf,KAAK,QAAQ;IACb,aAAa,IAAI,oBAAoB,YAAY;GACnD,CAAC;EAEL,OAAO,IAAI,SAAS,oBAAoB,KAAK,gBAAgB,GAC3D,UAAU,KAAK;GACb,OAAO,KAAK,KAAK;GACjB,KAAK,KAAK,KAAK;GACf,aAAa,GAAG,oBAAoB;EACtC,CAAC;OACI,IACL,SAAS,uBACT,qBACA,kBAAkB,SAAS,GAC3B;GACA,MAAM,YAAY,IAAI,kBAAkB;GACxC,MAAM,QAAQ,kBAAkB,GAAG;GACnC,MAAM,MAAM,kBAAkB,kBAAkB,SAAS,GAAG;GAC5D,UAAU,KAAK;IAAE;IAAO;IAAK,aAAa;GAAU,CAAC;EACvD,OAAO,IACL,SAAS,mBACT,aACA,qBACA,kBAAkB,SAAS,GAC3B;GAUA,MAAM,YAAY,IAAI,kBAAkB,KATvB,UACd,KAAK,mBAAmB;IACvB,MAAM,CAAC,KAAK,iBAAiB,eAC1B,MAAM,GAAG,EACT,KAAK,SAAS,KAAK,KAAK,CAAC;IAC5B,OAAO,GAAG,IAAI,IAAI;GACpB,CAAC,EACA,KAAK,IAE4C,EAAE;GACtD,MAAM,QAAQ,kBAAkB,GAAG;GACnC,MAAM,MAAM,kBAAkB,kBAAkB,SAAS,GAAG;GAC5D,UAAU,KAAK;IAAE;IAAO;IAAK,aAAa;GAAU,CAAC;EACvD,OAAO,IAAI,SAAS,sBAAsB,KAAK,kBAAkB,GAAG;GAClE,IAAI,cAAc,GAAG;GAErB,IAAI,aAAa,UAAU,SAAS,GAAG;IACrC,MAAM,WAAW,UACd,KAAK,mBAAmB;KACvB,MAAM,CAAC,KAAK,iBAAiB,eAC1B,MAAM,GAAG,EACT,KAAK,SAAS,KAAK,KAAK,CAAC;KAC5B,OAAO,GAAG,IAAI,IAAI;IACpB,CAAC,EACA,KAAK,IAAI;IACZ,eAAe,MAAM,SAAS;GAChC,OACE,eAAe;GAGjB,UAAU,KAAK;IACb,OAAO,KAAK,KAAK;IACjB,KAAK,KAAK,KAAK;IACf;GACF,CAAC;EACH;CACF;CAEA,IAAI,mBAAmB;CACvB,IAAI,mBAAmB;CAEvB,KAAK,MAAM,iBAAiB,wBAAwB;EAClD,MAAM,WAAW,gBAAgB,IAAI,cAAc,IAAI;EACvD,MAAM,eAAe,kBAAkB,IAAI,cAAc,IAAI;EAE7D,IAAI,cAAc;GAChB,IAAI,aAAa,SAAS,eAAe,mBAAmB;GAE5D,IAAI,aAAa,SAAS,eAAe,mBAAmB;GAI5D,IAAI,aAAa,kBAAkB,aAAa,mBAAmB;IACjE,MAAM,6BAAa,IAAI,IAAY;IAEnC,KAAK,MAAM,EAAE,MAAM,OAAO,KAAK,UAAU,cAAc;KACrD,IAAI,UAA2B;KAE/B,OAAO,SAAS;MACd,IAAI,QAAQ,SAAS,cAAc,MAAM;OACvC,WAAW,IAAI,IAAI;OACnB;MACF;MACA,UAAU,QAAQ;KACpB;IACF;IAEA,MAAM,cAAc,CAAC,GAAG,UAAU,EAAE,QACjC,QAAQ,CAAC,aAAa,yBAAyB,SAAS,GAAG,CAC9D;IAEA,IAAI,YAAY,SAAS,GAAG;KAC1B,MAAM,EAAE,sBAAsB;KAG9B,MAAM,WACJ,kBAAkB,WAChB,kBAAkB,WAAW,SAAS;KAE1C,UAAU,KAAK;MACb,OAAO,SAAS;MAChB,KAAK,SAAS;MACd,aAAa,KAAK,YAAY,KAAK,IAAI;KACzC,CAAC;IACH;GACF;EACF,OAAO;GACL,MAAM,OAAO,QAAQ,IAAI,cAAc,IAAI,KAAK;GAEhD,IAAI,SAAS,eAAe,mBAAmB;GAE/C,IAAI,SAAS,eAAe,mBAAmB;GAG/C,MAAM,mBAAmB,aADL,kBAAkB,IAAI,aAAa,KAAK,UACV,KAAK,KAAK,IAAI,SAAS;GAEzE,IAAI,cAAc,UAAU,GAAG;IAE7B,IAAI,YAAY;IAChB,IAAI,cAAc,KAAK,WAAW,SAAS,GACzC,YACE,cAAc,KAAK,WACjB,cAAc,KAAK,WAAW,SAAS,GACvC;IAEN,KAAK,MAAM,QAAQ,cAAc,KAAK,MACpC,IAAI,EAAE,oBAAoB,IAAI,GAC5B,YAAY,KAAK,IAAI,WAAW,KAAK,GAAI;IAI7C,IAAI,cAAc,KAAK,cAAc,KAAK,KAAK,SAAS,GACtD,YAAY,cAAc,KAAK,KAAK,GAAG;IAGzC,UAAU,KAAK;KACb,OAAO;KACP,KAAK;KACL,aAAa,KAAK;IACpB,CAAC;IACD;GACF;GAEA,MAAM,WAAW,cAAc,IAAI,MAAM;GAEzC,IAAI,SAAS,iBAAiB,GAC5B,UAAU,KAAK;IACb,OAAO,SAAS,KAAK,QAAS;IAC9B,KAAK,SAAS,KAAK,QAAS;IAC5B,aAAa;GACf,CAAC;QACI,IAAI,SAAS,aAAa,GAAG;IAClC,MAAM,QAAQ,SAAS,KAAK;IAC5B,MAAM,MAAM,SAAS,KAAK;IAI1B,IAAI,aAAa;IACjB,IAAI,WAAW;IAEf,KAAK,IAAI,IAAI,QAAQ,GAAG,KAAK,GAAG,KAAK;KACnC,MAAM,OAAO,SAAS;KACtB,IAAI,SAAS,KAAK;MAChB,aAAa;MACb;KACF;KACA,IAAI,SAAS,OAAO,SAAS,QAAQ,SAAS,QAAQ,SAAS,KAC7D;IACJ;IAEA,IAAI,eAAe,IACjB,KAAK,IAAI,IAAI,KAAK,IAAI,SAAS,QAAQ,KAAK;KAC1C,MAAM,OAAO,SAAS;KACtB,IAAI,SAAS,KAAK;MAChB,WAAW;MACX;KACF;KACA,IAAI,SAAS,OAAO,SAAS,QAAQ,SAAS,QAAQ,SAAS,KAC7D;IACJ;IAGF,IAAI,eAAe,MAAM,aAAa,IAAI;KAIxC,UAAU,KAAK;MACb,OAAO;MACP,KAAK,WAAW;MAChB,aAAa;KACf,CAAC;KACD,UAAU,KAAK;MACb,OAAO;MACP,KAAK,aAAa;MAClB,aAAa,wBAAwB,KAAK,IAAI,SAAS;KACzD,CAAC;IACH,OAAO;KACL,UAAU,KAAK;MACb,OAAO;MACF;MACL,aAAa;KACf,CAAC;KACD,UAAU,KAAK;MACN;MACP,KAAK;MACL,aAAa,wBAAwB,KAAK,IAAI,SAAS;KACzD,CAAC;IACH;GACF;EACF;CACF;CAEA,MAAM,gBAAgB,UAAkB,kBAA0B;EAChE,IAAI;EAEJ,SAAS,KAAK,EACZ,kBAAkB,MAAM;GACtB,IAAI,KAAK,KAAK,OAAO,UAAU,eAAe;IAC5C,qBAAqB;IACrB,KAAK,KAAK;GACZ;EACF,EACF,CAAC;EAED,IAAI,CAAC,oBAAoB;GACvB,MAAM,eAAe,YAAY,SAAS,WAAW,cAAc;GACnE,IAAI,YAAY;GAEhB,IAAI,IAAI,QAAQ,KAAK,SAAS,GAC5B,YAAY,IAAI,QAAQ,KAAK,GAAG;QAC3B,IAAI,IAAI,QAAQ,cAAc,IAAI,QAAQ,WAAW,SAAS,GAAG;IACtE,YACE,IAAI,QAAQ,WAAW,IAAI,QAAQ,WAAW,SAAS,GAAG;IAE5D,IAAI,SAAS,eAAe,KAAK;IAEjC,UAAU,KAAK;KACb,OAAO;KACP,KAAK;KACL,aAAa,KAAK;IACpB,CAAC;IACD;GACF;GAEA,IACE,cAAc,KACb,IAAI,QAAQ,KAAK,SAAS,KACzB,cAAc,IAAI,QAAQ,KAAK,GAAG,OAEpC,UAAU,KAAK;IACb,OAAO;IACP,KAAK;IACL,aAAa;GACf,CAAC;EAEL,OASE,IAAI,CARuB,mBAAmB,KAAK,WACT,MACvC,cACC,EAAE,kBAAkB,SAAS,KAC7B,EAAE,aAAa,UAAU,QAAQ,KACjC,UAAU,SAAS,SAAS,QAChC,GAEmB;GACjB,MAAM,aAAa,SAAS,MAC1B,mBAAmB,KAAK,OACxB,mBAAmB,KAAK,GAC1B;GACA,MAAM,oBAAoB,WAAW,YAAY,GAAG;GAEpD,IAAI,sBAAsB,IAAI;IAC5B,MAAM,gBAAgB,CAAC,WACpB,MAAM,GAAG,iBAAiB,EAC1B,KAAK,EACL,SAAS,GAAG;IACf,MAAM,cACJ,mBAAmB,KAAK,QAAS;IACnC,MAAM,SAAS,gBAAgB,OAAO;IACtC,UAAU,KAAK;KACb,OAAO;KACP,KAAK;KACL,aAAa,GAAG,SAAS,SAAS;IACpC,CAAC;GACH;EACF;CAEJ;CAEA,IAAI,kBAAkB,aAAa,eAAe,oBAAoB;CAEtE,IAAI,kBAAkB,aAAa,eAAe,UAAU;CAE5D,UAAU,MAAM,OAAO,UAAU;EAC/B,IAAI,MAAM,UAAU,MAAM,OAAO,OAAO,MAAM,QAAQ,MAAM;EAE5D,OAAO,MAAM,MAAM,MAAM;CAC3B,CAAC;CAED,IAAI,gBAAgB;CAEpB,KAAK,MAAM,QAAQ,WACjB,gBACE,cAAc,MAAM,GAAG,KAAK,KAAK,IACjC,KAAK,cACL,cAAc,MAAM,KAAK,GAAG;CAGhC,IAAI,MAAM;EACR,cAAc,UAAU,aAAa;EAErC,MAAM,gBAAgB,oBAAoB,aAAa;EAEvD,IAAI,eACF,IAAI;GACF,SAAS,cAAc,QAAQ,YAAY,QAAQ,GAAG;IACpD,OAAO;IACP,KAAK,cAAc,OAAO;GAC5B,CAAC;EACH,SAAS,OAAO;GACd,QAAQ,MAAM,KAAK;EACrB;CAEJ;CAEA,OAAO;EAAE;EAAkB,cAAc;CAAc;AACzD"}
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\n/**\n * Returns true when a string literal sits inside a JSX child expression\n * container (i.e. a React node slot), meaning content.key renders as an\n * IntlayerNode and should NOT receive a .value suffix.\n * Returns false for attribute-value containers and non-JSX contexts.\n */\nconst isInJsxNodeContext = (path: NodePath): boolean => {\n let current: NodePath | null = path.parentPath;\n while (current) {\n if (current.isJSXExpressionContainer()) {\n return !current.parentPath?.isJSXAttribute();\n }\n if (current.isFunction() || current.isProgram()) return false;\n current = current.parentPath;\n }\n return false;\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 // Angular exposes content via a reactive signal — access is `content().key`.\n // solid-intlayer now returns a Proxy that supports direct `content.key` access.\n const usesSignalAccessor = 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 const componentPath = componentNodeToPath.get(current.node)!;\n\n const existingInfo = existingInfoCache.get(current.node);\n if (existingInfo) {\n return existingInfo;\n }\n\n if (componentsNeedingHooks.has(componentPath)) {\n return undefined;\n }\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 const generatedVarNames = new Map<NodePath, string>();\n for (const componentPath of componentsNeedingHooks) {\n let varName = 'content';\n let counter = 1;\n while (componentPath.scope.hasBinding(varName)) {\n varName = `content${counter}`;\n counter++;\n }\n generatedVarNames.set(componentPath, varName);\n }\n\n const getProvidingVarName = (path: NodePath): string => {\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 if (existingInfo) {\n return existingInfo.variableName ?? 'content';\n }\n if (componentsNeedingHooks.has(componentPath)) {\n return generatedVarNames.get(componentPath) || 'content';\n }\n }\n current = current.parentPath;\n }\n return 'content';\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 ?? getProvidingVarName(path);\n const contentAccessCode = existingInfo?.isDestructured\n ? key\n : usesSignalAccessor\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 const suffix = isInJsxNodeContext(path) ? '' : valueSuffix;\n textEdits.push({\n start: path.node.start!,\n end: path.node.end!,\n replacement: `${contentAccessCode}${suffix}`,\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 hookVarName = generatedVarNames.get(componentPath) || 'content';\n const hookStatementStr = `\\n const ${hookVarName} = ${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 if (insertPos === 0 && componentPath.node.body.length > 0) {\n insertPos = componentPath.node.body[0].start!;\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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"}
@@ -12,7 +12,7 @@ const shouldExtract = (text) => {
12
12
  const trimmed = text.trim();
13
13
  if (!trimmed) return false;
14
14
  if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(trimmed)) return false;
15
- if (trimmed.startsWith("{") || trimmed.startsWith("v-")) return false;
15
+ if (trimmed.startsWith("{") && !trimmed.startsWith("{{") || trimmed.startsWith("v-")) return false;
16
16
  if (trimmed.includes("=>") || trimmed.includes(");") || trimmed.includes("(){") || trimmed.includes("==") || trimmed.includes("window.") || trimmed.startsWith("(function") || trimmed.startsWith("function(")) return false;
17
17
  if ((trimmed.match(/[^\p{L}\p{N}\s.,!?;:'"()[\]{}–—/«»„“\p{Sc}%&*+#@^_+=<>/~]/gu) || []).length > 5) return false;
18
18
  const wordCount = trimmed.split(/\s+/).length;
@@ -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 * - 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 // Ignore explicit code patterns (markers)\n if (\n trimmed.includes('=>') ||\n trimmed.includes(');') ||\n trimmed.includes('(){') ||\n trimmed.includes('==') ||\n trimmed.includes('window.') ||\n trimmed.startsWith('(function') ||\n trimmed.startsWith('function(')\n ) {\n return false;\n }\n\n // Heuristic: check for characters that are common in code but rare in natural text.\n // Whitelist: letters, numbers, spaces, and frequent text symbols (including punctuation, braces, and technical symbols)\n const nonTextualRegex =\n /[^\\p{L}\\p{N}\\s.,!?;:'\"()[\\]{}–—/«»„“\\p{Sc}%&*+#@^_+=<>/~]/gu;\n const nonTextualMatches = trimmed.match(nonTextualRegex) || [];\n\n // If a string contains a high density of truly exceptional symbols (like |, \\, etc.),\n // it is highly likely to be code or complex technical data.\n if (nonTextualMatches.length > 5) return false;\n\n const wordCount = trimmed.split(/\\s+/).length;\n\n // Ignore CSS/Tailwind utility class strings. A string whose tokens all look\n // like CSS utility classes (lowercase, optional responsive/state prefix like\n // \"sm:\" or \"hover:\", optional hyphenated suffix like \"-4\" or \"-full\") and\n // where at least one token contains a hyphen is almost certainly a className\n // value, not translatable text.\n const cssClassTokenRegex =\n /^!?([a-z][a-z0-9-]*:)*[a-z][a-z0-9]*(-[a-z0-9[\\].,%#/]+)*(\\/[a-z0-9]+)?$/;\n if (wordCount > 1) {\n const tokens = trimmed.split(/\\s+/);\n if (\n tokens.every((token) => cssClassTokenRegex.test(token)) &&\n tokens.some((token) => token.includes('-'))\n ) {\n return false;\n }\n }\n\n // Check if starts with a capital letter (including after an opening parenthesis/quote)\n const isCapitalized = /^['\"([]*\\p{Lu}/u.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]\\p{Lu}/u.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,KAAK;CAE1B,IAAI,CAAC,SAAS,OAAO;CAGrB,IAAI,6BAA6B,KAAK,OAAO,GAAG,OAAO;CAGvD,IAAI,QAAQ,WAAW,GAAG,KAAK,QAAQ,WAAW,IAAI,GAAG,OAAO;CAGhE,IACE,QAAQ,SAAS,IAAI,KACrB,QAAQ,SAAS,IAAI,KACrB,QAAQ,SAAS,KAAK,KACtB,QAAQ,SAAS,IAAI,KACrB,QAAQ,SAAS,SAAS,KAC1B,QAAQ,WAAW,WAAW,KAC9B,QAAQ,WAAW,WAAW,GAE9B,OAAO;CAWT,KAJ0B,QAAQ,MAAM,6DAAe,KAAK,CAAC,GAIvC,SAAS,GAAG,OAAO;CAEzC,MAAM,YAAY,QAAQ,MAAM,KAAK,EAAE;CAOvC,MAAM,qBACJ;CACF,IAAI,YAAY,GAAG;EACjB,MAAM,SAAS,QAAQ,MAAM,KAAK;EAClC,IACE,OAAO,OAAO,UAAU,mBAAmB,KAAK,KAAK,CAAC,KACtD,OAAO,MAAM,UAAU,MAAM,SAAS,GAAG,CAAC,GAE1C,OAAO;CAEX;CAGA,MAAM,gBAAgB,kBAAkB,KAAK,OAAO;CAGpD,IAAI,cAAc,GAAG;EAEnB,IAAI,eAAe,KAAK,OAAO,GAAG,OAAO;EAEzC,IAAI,QAAQ,SAAS,GAAG,KAAK,QAAQ,SAAS,GAAG,GAAG,OAAO;CAC7D;CAIA,IAAI,CAAC,iBAAiB,aAAa,GAAG,OAAO;CAE7C,OAAO;AACT"}
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, but allow {{placeholder}} which is intlayer's insertion format\n if (\n (trimmed.startsWith('{') && !trimmed.startsWith('{{')) ||\n trimmed.startsWith('v-')\n )\n return false;\n\n // Ignore explicit code patterns (markers)\n if (\n trimmed.includes('=>') ||\n trimmed.includes(');') ||\n trimmed.includes('(){') ||\n trimmed.includes('==') ||\n trimmed.includes('window.') ||\n trimmed.startsWith('(function') ||\n trimmed.startsWith('function(')\n ) {\n return false;\n }\n\n // Heuristic: check for characters that are common in code but rare in natural text.\n // Whitelist: letters, numbers, spaces, and frequent text symbols (including punctuation, braces, and technical symbols)\n const nonTextualRegex =\n /[^\\p{L}\\p{N}\\s.,!?;:'\"()[\\]{}–—/«»„“\\p{Sc}%&*+#@^_+=<>/~]/gu;\n const nonTextualMatches = trimmed.match(nonTextualRegex) || [];\n\n // If a string contains a high density of truly exceptional symbols (like |, \\, etc.),\n // it is highly likely to be code or complex technical data.\n if (nonTextualMatches.length > 5) return false;\n\n const wordCount = trimmed.split(/\\s+/).length;\n\n // Ignore CSS/Tailwind utility class strings. A string whose tokens all look\n // like CSS utility classes (lowercase, optional responsive/state prefix like\n // \"sm:\" or \"hover:\", optional hyphenated suffix like \"-4\" or \"-full\") and\n // where at least one token contains a hyphen is almost certainly a className\n // value, not translatable text.\n const cssClassTokenRegex =\n /^!?([a-z][a-z0-9-]*:)*[a-z][a-z0-9]*(-[a-z0-9[\\].,%#/]+)*(\\/[a-z0-9]+)?$/;\n if (wordCount > 1) {\n const tokens = trimmed.split(/\\s+/);\n if (\n tokens.every((token) => cssClassTokenRegex.test(token)) &&\n tokens.some((token) => token.includes('-'))\n ) {\n return false;\n }\n }\n\n // Check if starts with a capital letter (including after an opening parenthesis/quote)\n const isCapitalized = /^['\"([]*\\p{Lu}/u.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]\\p{Lu}/u.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,KAAK;CAE1B,IAAI,CAAC,SAAS,OAAO;CAGrB,IAAI,6BAA6B,KAAK,OAAO,GAAG,OAAO;CAGvD,IACG,QAAQ,WAAW,GAAG,KAAK,CAAC,QAAQ,WAAW,IAAI,KACpD,QAAQ,WAAW,IAAI,GAEvB,OAAO;CAGT,IACE,QAAQ,SAAS,IAAI,KACrB,QAAQ,SAAS,IAAI,KACrB,QAAQ,SAAS,KAAK,KACtB,QAAQ,SAAS,IAAI,KACrB,QAAQ,SAAS,SAAS,KAC1B,QAAQ,WAAW,WAAW,KAC9B,QAAQ,WAAW,WAAW,GAE9B,OAAO;CAWT,KAJ0B,QAAQ,MAAM,6DAAe,KAAK,CAAC,GAIvC,SAAS,GAAG,OAAO;CAEzC,MAAM,YAAY,QAAQ,MAAM,KAAK,EAAE;CAOvC,MAAM,qBACJ;CACF,IAAI,YAAY,GAAG;EACjB,MAAM,SAAS,QAAQ,MAAM,KAAK;EAClC,IACE,OAAO,OAAO,UAAU,mBAAmB,KAAK,KAAK,CAAC,KACtD,OAAO,MAAM,UAAU,MAAM,SAAS,GAAG,CAAC,GAE1C,OAAO;CAEX;CAGA,MAAM,gBAAgB,kBAAkB,KAAK,OAAO;CAGpD,IAAI,cAAc,GAAG;EAEnB,IAAI,eAAe,KAAK,OAAO,GAAG,OAAO;EAEzC,IAAI,QAAQ,SAAS,GAAG,KAAK,QAAQ,SAAS,GAAG,GAAG,OAAO;CAC7D;CAIA,IAAI,CAAC,iBAAiB,aAAa,GAAG,OAAO;CAE7C,OAAO;AACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"processTsxFile.d.ts","names":[],"sources":["../../../src/extractContent/processTsxFile.ts"],"mappings":";;;;KAeY,QAAA;EACV,KAAA;EACA,GAAA;EACA,WAAA;AAAA;;;;cAUW,cAAA,GACX,QAAA,UACA,YAAA,UACA,WAAA,EAAa,WAAA,EACb,aAAA,EAAe,cAAA,EACf,IAAA,YACA,oBAAA,GAAsB,MAAA,mBACtB,gBAAA;EAEA,gBAAA,EAAkB,MAAA,SAAe,MAAA;EACjC,YAAA;AAAA"}
1
+ {"version":3,"file":"processTsxFile.d.ts","names":[],"sources":["../../../src/extractContent/processTsxFile.ts"],"mappings":";;;;KAeY,QAAA;EACV,KAAA;EACA,GAAA;EACA,WAAA;AAAA;;;;cA4BW,cAAA,GACX,QAAA,UACA,YAAA,UACA,WAAA,EAAa,WAAA,EACb,aAAA,EAAe,cAAA,EACf,IAAA,YACA,oBAAA,GAAsB,MAAA,mBACtB,gBAAA;EAEA,gBAAA,EAAkB,MAAA,SAAe,MAAA;EACjC,YAAA;AAAA"}
@@ -4,8 +4,8 @@
4
4
  *
5
5
  * Example: "src/components/MyComponent/index.tsx" -> "comp-my-component"
6
6
  */
7
- declare const extractDictionaryKeyFromPath: (filePath: string, prefix?: any) => string;
8
- declare const extractDictionaryKey: (filePath: string, fileText: string, prefix?: any) => string;
7
+ declare const extractDictionaryKeyFromPath: (filePath: string, prefix?: string) => string;
8
+ declare const extractDictionaryKey: (filePath: string, fileText: string, prefix?: string) => string;
9
9
  //#endregion
10
10
  export { extractDictionaryKey, extractDictionaryKeyFromPath };
11
11
  //# sourceMappingURL=extractDictionaryKey.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intlayer/babel",
3
- "version": "8.10.1",
3
+ "version": "8.11.0-canary.0",
4
4
  "private": false,
5
5
  "description": "A Babel plugin for Intlayer that transforms declaration files and provides internationalization features during the build process according to the Intlayer configuration.",
6
6
  "keywords": [
@@ -81,12 +81,12 @@
81
81
  "@babel/plugin-syntax-jsx": "7.28.6",
82
82
  "@babel/traverse": "7.29.0",
83
83
  "@babel/types": "7.29.0",
84
- "@intlayer/chokidar": "8.10.1",
85
- "@intlayer/config": "8.10.1",
86
- "@intlayer/core": "8.10.1",
87
- "@intlayer/dictionaries-entry": "8.10.1",
88
- "@intlayer/types": "8.10.1",
89
- "@intlayer/unmerged-dictionaries-entry": "8.10.1",
84
+ "@intlayer/chokidar": "8.11.0-canary.0",
85
+ "@intlayer/config": "8.11.0-canary.0",
86
+ "@intlayer/core": "8.11.0-canary.0",
87
+ "@intlayer/dictionaries-entry": "8.11.0-canary.0",
88
+ "@intlayer/types": "8.11.0-canary.0",
89
+ "@intlayer/unmerged-dictionaries-entry": "8.11.0-canary.0",
90
90
  "@types/babel__core": "7.20.5",
91
91
  "@types/babel__generator": "7.27.0",
92
92
  "@types/babel__traverse": "7.28.0"
@@ -104,8 +104,8 @@
104
104
  "vitest": "4.1.7"
105
105
  },
106
106
  "peerDependencies": {
107
- "@intlayer/svelte-compiler": "8.10.1",
108
- "@intlayer/vue-compiler": "8.10.1"
107
+ "@intlayer/svelte-compiler": "8.11.0-canary.0",
108
+ "@intlayer/vue-compiler": "8.11.0-canary.0"
109
109
  },
110
110
  "peerDependenciesMeta": {
111
111
  "@intlayer/svelte-compiler": {