@intlayer/babel 8.9.7 → 8.10.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.
- package/README.md +14 -0
- package/dist/cjs/babel-plugin-intlayer-extract.cjs.map +1 -1
- package/dist/cjs/babel-plugin-intlayer-field-rename.cjs.map +1 -1
- package/dist/cjs/babel-plugin-intlayer-optimize.cjs.map +1 -1
- package/dist/cjs/babel-plugin-intlayer-usage-analyzer.cjs.map +1 -1
- package/dist/cjs/extractContent/babelProcessor.cjs +12 -2
- package/dist/cjs/extractContent/babelProcessor.cjs.map +1 -1
- package/dist/cjs/extractContent/contentWriter.cjs.map +1 -1
- package/dist/cjs/extractContent/extractContent.cjs.map +1 -1
- package/dist/cjs/extractContent/processTsxFile.cjs.map +1 -1
- package/dist/cjs/extractContent/utils/constants.cjs.map +1 -1
- package/dist/cjs/extractContent/utils/detectPackageName.cjs.map +1 -1
- package/dist/cjs/extractContent/utils/extractDictionaryInfo.cjs.map +1 -1
- package/dist/cjs/extractContent/utils/extractDictionaryKey.cjs.map +1 -1
- package/dist/cjs/extractContent/utils/generateKey.cjs.map +1 -1
- package/dist/cjs/extractContent/utils/getComponentName.cjs.map +1 -1
- package/dist/cjs/extractContent/utils/getExistingIntlayerInfo.cjs.map +1 -1
- package/dist/cjs/extractContent/utils/getOrGenerateKey.cjs.map +1 -1
- package/dist/cjs/extractContent/utils/resolveDictionaryKey.cjs.map +1 -1
- package/dist/cjs/extractContent/utils/shouldExtract.cjs +5 -0
- package/dist/cjs/extractContent/utils/shouldExtract.cjs.map +1 -1
- package/dist/cjs/extractScriptBlocks.cjs.map +1 -1
- package/dist/cjs/getExtractPluginOptions.cjs.map +1 -1
- package/dist/cjs/getOptimizePluginOptions.cjs.map +1 -1
- package/dist/cjs/transformers.cjs.map +1 -1
- package/dist/esm/babel-plugin-intlayer-extract.mjs.map +1 -1
- package/dist/esm/babel-plugin-intlayer-field-rename.mjs.map +1 -1
- package/dist/esm/babel-plugin-intlayer-optimize.mjs.map +1 -1
- package/dist/esm/babel-plugin-intlayer-usage-analyzer.mjs.map +1 -1
- package/dist/esm/extractContent/babelProcessor.mjs +12 -2
- package/dist/esm/extractContent/babelProcessor.mjs.map +1 -1
- package/dist/esm/extractContent/contentWriter.mjs.map +1 -1
- package/dist/esm/extractContent/extractContent.mjs.map +1 -1
- package/dist/esm/extractContent/processTsxFile.mjs.map +1 -1
- package/dist/esm/extractContent/utils/constants.mjs.map +1 -1
- package/dist/esm/extractContent/utils/detectPackageName.mjs.map +1 -1
- package/dist/esm/extractContent/utils/extractDictionaryInfo.mjs.map +1 -1
- package/dist/esm/extractContent/utils/extractDictionaryKey.mjs.map +1 -1
- package/dist/esm/extractContent/utils/generateKey.mjs.map +1 -1
- package/dist/esm/extractContent/utils/getComponentName.mjs.map +1 -1
- package/dist/esm/extractContent/utils/getExistingIntlayerInfo.mjs.map +1 -1
- package/dist/esm/extractContent/utils/getOrGenerateKey.mjs.map +1 -1
- package/dist/esm/extractContent/utils/resolveDictionaryKey.mjs.map +1 -1
- package/dist/esm/extractContent/utils/shouldExtract.mjs +5 -0
- package/dist/esm/extractContent/utils/shouldExtract.mjs.map +1 -1
- package/dist/esm/extractScriptBlocks.mjs.map +1 -1
- package/dist/esm/getExtractPluginOptions.mjs.map +1 -1
- package/dist/esm/getOptimizePluginOptions.mjs.map +1 -1
- package/dist/esm/transformers.mjs.map +1 -1
- package/dist/types/babel-plugin-intlayer-extract.d.ts.map +1 -1
- package/dist/types/babel-plugin-intlayer-field-rename.d.ts.map +1 -1
- package/dist/types/babel-plugin-intlayer-optimize.d.ts.map +1 -1
- package/dist/types/babel-plugin-intlayer-usage-analyzer.d.ts.map +1 -1
- package/dist/types/extractContent/babelProcessor.d.ts.map +1 -1
- package/dist/types/extractContent/contentWriter.d.ts.map +1 -1
- package/dist/types/extractContent/extractContent.d.ts.map +1 -1
- package/dist/types/extractContent/utils/constants.d.ts.map +1 -1
- package/dist/types/extractContent/utils/detectPackageName.d.ts.map +1 -1
- package/dist/types/extractContent/utils/extractDictionaryInfo.d.ts.map +1 -1
- package/dist/types/extractContent/utils/extractDictionaryKey.d.ts.map +1 -1
- package/dist/types/extractContent/utils/generateKey.d.ts.map +1 -1
- package/dist/types/extractContent/utils/getComponentName.d.ts.map +1 -1
- package/dist/types/extractContent/utils/getExistingIntlayerInfo.d.ts.map +1 -1
- package/dist/types/extractContent/utils/shouldExtract.d.ts.map +1 -1
- package/dist/types/extractScriptBlocks.d.ts.map +1 -1
- package/dist/types/getExtractPluginOptions.d.ts.map +1 -1
- package/dist/types/getOptimizePluginOptions.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/transformers.d.ts.map +1 -1
- package/package.json +10 -10
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"babelProcessor.cjs","names":["_traverse","t","shouldExtract","getOrGenerateKey","getExistingIntlayerInfo","resolveDictionaryKey","getComponentName","ATTRIBUTES_TO_EXTRACT"],"sources":["../../../src/extractContent/babelProcessor.ts"],"sourcesContent":["import _traverse, { type NodePath } from '@babel/traverse';\nimport * as t from '@babel/types';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { resolveDictionaryKey } from '../extractContent/utils';\nimport {\n ATTRIBUTES_TO_EXTRACT,\n getComponentName,\n getExistingIntlayerInfo,\n getOrGenerateKey,\n shouldExtract,\n} from './utils';\n\nexport type BabelReplacement = {\n path: NodePath;\n key: string;\n type:\n | 'jsx-text'\n | 'jsx-attribute'\n | 'string-literal'\n | 'jsx-insertion'\n | 'jsx-text-combined'\n | 'template-literal';\n componentKey: string;\n childrenToReplace?: t.Node[];\n variables?: string[];\n};\n\n// CJS/ESM interop: @babel/traverse exports its function as `.default` in CJS bundles\nconst traverse = (\n typeof _traverse === 'function' ? _traverse : (_traverse as any).default\n) as typeof _traverse;\n\n/**\n * Handles JSX insertions (elements with multiple children, including expressions).\n * Replaces complex JSX structures with variable-based translations.\n */\nexport const handleJsxInsertionBabel = (\n path: NodePath<t.JSXElement | t.JSXFragment>,\n fileCode: string,\n existingKeys: Set<string>,\n getComponentKeyForPath: (path: NodePath) => string,\n extractedContent: Record<string, Record<string, string>>,\n replacements: BabelReplacement[],\n handledNodes: Set<t.Node>\n): boolean => {\n const children = path.node.children;\n\n if (children.length <= 1) return false;\n\n const parts: {\n type: 'text' | 'var';\n value: string;\n originalExpr?: string;\n }[] = [];\n let hasSignificantText = false;\n let hasVariables = false;\n\n for (const child of children) {\n if (t.isJSXText(child)) {\n const text = child.value;\n\n if (text.trim().length > 0) hasSignificantText = true;\n\n parts.push({ type: 'text', value: text });\n } else if (t.isJSXExpressionContainer(child)) {\n if (t.isJSXEmptyExpression(child.expression)) {\n parts.push({ type: 'text', value: '' });\n } else {\n const expr = child.expression;\n\n if (t.isIdentifier(expr)) {\n parts.push({\n type: 'var',\n value: expr.name,\n originalExpr: expr.name,\n });\n hasVariables = true;\n } else if (t.isMemberExpression(expr)) {\n const code = fileCode.substring(expr.start!, expr.end!);\n\n const varName = t.isIdentifier(expr.property)\n ? expr.property.name\n : 'var';\n\n parts.push({ type: 'var', value: varName, originalExpr: code });\n\n hasVariables = true;\n } else if (t.isTemplateLiteral(expr)) {\n for (let i = 0; i < expr.quasis.length; i++) {\n parts.push({ type: 'text', value: expr.quasis[i].value.raw });\n if (i < expr.expressions.length) {\n const subExpr = expr.expressions[i];\n if (t.isIdentifier(subExpr)) {\n parts.push({\n type: 'var',\n value: subExpr.name,\n originalExpr: subExpr.name,\n });\n hasVariables = true;\n } else if (t.isMemberExpression(subExpr)) {\n const code = fileCode.substring(subExpr.start!, subExpr.end!);\n const varName = t.isIdentifier(subExpr.property)\n ? subExpr.property.name\n : 'var';\n parts.push({ type: 'var', value: varName, originalExpr: code });\n hasVariables = true;\n } else {\n return false;\n }\n }\n }\n } else {\n return false;\n }\n }\n } else {\n return false;\n }\n }\n\n if (!hasSignificantText) return false;\n\n let combinedString = '';\n for (const part of parts) {\n if (part.type === 'var') combinedString += `{{${part.value}}}`;\n else combinedString += part.value;\n }\n\n const cleanString = combinedString.replace(/\\s+/g, ' ').trim();\n\n if (shouldExtract(cleanString)) {\n const componentKey = getComponentKeyForPath(path);\n const key = getOrGenerateKey(\n cleanString,\n componentKey,\n existingKeys,\n extractedContent\n );\n\n const varMap = parts\n .filter((part) => part.type === 'var')\n .map((part) => `${part.value}: ${part.originalExpr}`);\n const uniqueVars = Array.from(new Set(varMap));\n\n if (hasVariables) {\n replacements.push({\n path,\n key,\n type: 'jsx-insertion',\n componentKey,\n childrenToReplace: children,\n variables: uniqueVars,\n });\n } else {\n replacements.push({\n path,\n key,\n type: 'jsx-text-combined',\n componentKey,\n childrenToReplace: children,\n });\n }\n\n children.forEach((child) => {\n handledNodes.add(child);\n });\n return true;\n }\n\n return false;\n};\n\n/**\n * Traverses the AST to identify components and extract content.\n * Returns extraction results and metadata about which components need Intlayer hooks.\n */\nexport const extractBabelContentForComponents = (\n ast: t.File,\n fileCode: string,\n existingKeys: Set<string>,\n defaultKey: string = 'default',\n configuration: IntlayerConfig,\n filePath: string,\n unmergedDictionaries: Record<string, unknown> = {}\n): {\n extractedContent: Record<string, Record<string, string>>;\n replacements: BabelReplacement[];\n componentsNeedingHooks: Set<NodePath>;\n componentKeyMap: Map<t.Node, string>;\n componentPaths: NodePath[];\n hookMap: Map<t.Node, 'useIntlayer' | 'getIntlayer'>;\n isSolid: boolean;\n} => {\n const extractedContent: Record<string, Record<string, string>> = {};\n const replacements: BabelReplacement[] = [];\n const handledNodes = new Set<t.Node>();\n const componentKeyMap = new Map<t.Node, string>();\n const hookMap = new Map<t.Node, 'useIntlayer' | 'getIntlayer'>();\n const usedKeysInFile = new Set<string>();\n let globalFileKey: string | undefined;\n\n const componentPaths: NodePath[] = [];\n\n traverse(ast, {\n Program(path) {\n componentPaths.push(path);\n },\n FunctionDeclaration(path) {\n componentPaths.push(path);\n },\n ArrowFunctionExpression(path) {\n componentPaths.push(path);\n },\n FunctionExpression(path) {\n componentPaths.push(path);\n },\n });\n\n for (const path of componentPaths) {\n const existingInfo = getExistingIntlayerInfo(path);\n\n if (existingInfo) {\n componentKeyMap.set(path.node, existingInfo.key);\n usedKeysInFile.add(existingInfo.key);\n hookMap.set(path.node, existingInfo.hook);\n } else {\n if (path.isProgram()) {\n if (!globalFileKey) {\n globalFileKey = resolveDictionaryKey(\n defaultKey,\n filePath,\n configuration,\n unmergedDictionaries,\n usedKeysInFile\n );\n usedKeysInFile.add(globalFileKey);\n }\n componentKeyMap.set(path.node, globalFileKey);\n hookMap.set(path.node, 'getIntlayer');\n } else {\n let inheritedKey: string | undefined;\n let parent: NodePath | null = path.parentPath;\n while (parent) {\n if (componentKeyMap.has(parent.node)) {\n inheritedKey = componentKeyMap.get(parent.node);\n break;\n }\n parent = parent.parentPath;\n }\n\n if (!inheritedKey) {\n if (!globalFileKey) {\n globalFileKey = resolveDictionaryKey(\n defaultKey,\n filePath,\n configuration,\n unmergedDictionaries,\n usedKeysInFile\n );\n usedKeysInFile.add(globalFileKey);\n }\n inheritedKey = globalFileKey;\n }\n\n componentKeyMap.set(path.node, inheritedKey);\n\n const compName = getComponentName(path);\n const isComponent = compName ? /^[A-Z]/.test(compName) : false;\n const isHook = compName ? /^use[A-Z]/.test(compName) : false;\n hookMap.set(\n path.node,\n isComponent || isHook ? 'useIntlayer' : 'getIntlayer'\n );\n }\n }\n }\n\n const getComponentKeyForPath = (path: NodePath): string => {\n let current: NodePath | null = path;\n while (current) {\n if (componentKeyMap.has(current.node)) {\n return componentKeyMap.get(current.node)!;\n }\n current = current.parentPath;\n }\n return globalFileKey || defaultKey;\n };\n\n traverse(ast, {\n JSXElement(path) {\n if (handledNodes.has(path.node)) return;\n\n handleJsxInsertionBabel(\n path,\n fileCode,\n existingKeys,\n getComponentKeyForPath,\n extractedContent,\n replacements,\n handledNodes\n );\n },\n JSXFragment(path) {\n if (handledNodes.has(path.node)) return;\n\n handleJsxInsertionBabel(\n path,\n fileCode,\n existingKeys,\n getComponentKeyForPath,\n extractedContent,\n replacements,\n handledNodes\n );\n },\n JSXText(path) {\n if (handledNodes.has(path.node)) return;\n\n const text = path.node.value;\n\n if (shouldExtract(text)) {\n const componentKey = getComponentKeyForPath(path);\n const key = getOrGenerateKey(\n text.replace(/\\s+/g, ' ').trim(),\n componentKey,\n existingKeys,\n extractedContent\n );\n replacements.push({ path, key, type: 'jsx-text', componentKey });\n }\n },\n JSXAttribute(path) {\n if (handledNodes.has(path.node)) return;\n\n const name = path.node.name.name;\n\n if (\n typeof name !== 'string' ||\n !ATTRIBUTES_TO_EXTRACT.includes(name as any)\n )\n return;\n const value = path.node.value;\n\n if (t.isStringLiteral(value) && shouldExtract(value.value)) {\n const componentKey = getComponentKeyForPath(path);\n const key = getOrGenerateKey(\n value.value.trim(),\n componentKey,\n existingKeys,\n extractedContent\n );\n replacements.push({ path, key, type: 'jsx-attribute', componentKey });\n }\n },\n StringLiteral(path) {\n if (handledNodes.has(path.node)) return;\n\n const text = path.node.value;\n\n if (!shouldExtract(text)) return;\n\n const parent = path.parentPath;\n\n if (\n parent.isImportDeclaration() ||\n parent.isImportSpecifier() ||\n parent.isExportDeclaration()\n )\n return;\n\n if (parent.isJSXAttribute()) return;\n\n if (\n parent.isCallExpression() &&\n t.isMemberExpression(parent.node.callee)\n ) {\n if (\n t.isIdentifier(parent.node.callee.object) &&\n parent.node.callee.object.name === 'console' &&\n t.isIdentifier(parent.node.callee.property) &&\n parent.node.callee.property.name === 'log'\n ) {\n return;\n }\n }\n\n if (parent.isObjectProperty() && parent.node.key === path.node) return;\n\n if (parent.isMemberExpression() && parent.node.property === path.node)\n return;\n\n const componentKey = getComponentKeyForPath(path);\n const key = getOrGenerateKey(\n text.trim(),\n componentKey,\n existingKeys,\n extractedContent\n );\n replacements.push({ path, key, type: 'string-literal', componentKey });\n },\n TemplateLiteral(path) {\n if (handledNodes.has(path.node)) return;\n\n const { quasis, expressions } = path.node;\n\n // Build the combined string with placeholders\n let combinedString = '';\n const variables: string[] = [];\n let hasSignificantText = false;\n\n for (let i = 0; i < quasis.length; i++) {\n const text = quasis[i].value.raw;\n combinedString += text;\n if (text.trim().length > 0) hasSignificantText = true;\n\n if (i < expressions.length) {\n const expr = expressions[i];\n if (t.isIdentifier(expr)) {\n combinedString += `{{${expr.name}}}`;\n variables.push(`${expr.name}: ${expr.name}`);\n } else if (t.isMemberExpression(expr)) {\n const code = fileCode.substring(expr.start!, expr.end!);\n const varName = t.isIdentifier(expr.property)\n ? expr.property.name\n : 'var';\n combinedString += `{{${varName}}}`;\n variables.push(`${varName}: ${code}`);\n } else {\n // Complex expression in template literal, skip\n return;\n }\n }\n }\n\n if (!hasSignificantText) return;\n\n const cleanString = combinedString.replace(/\\s+/g, ' ').trim();\n\n if (!shouldExtract(cleanString)) return;\n\n const componentKey = getComponentKeyForPath(path);\n const key = getOrGenerateKey(\n cleanString,\n componentKey,\n existingKeys,\n extractedContent\n );\n\n const uniqueVars = Array.from(new Set(variables));\n\n replacements.push({\n path,\n key,\n type: 'template-literal',\n componentKey,\n variables: uniqueVars,\n });\n },\n });\n\n const componentsNeedingHooks = new Set<NodePath>();\n for (const componentPath of componentPaths) {\n if (componentPath.isProgram()) {\n const hasDirectReplacements = replacements.some((replacement) => {\n let current: NodePath | null = replacement.path;\n while (current) {\n if (current.node === componentPath.node) {\n return true;\n }\n const isOtherComponent = componentPaths.some(\n (p) => p !== componentPath && p.node === current?.node\n );\n if (isOtherComponent) {\n return false;\n }\n current = current.parentPath;\n }\n return false;\n });\n\n if (hasDirectReplacements) {\n componentsNeedingHooks.add(componentPath);\n }\n continue;\n }\n\n const hasReplacements = replacements.some((replacement) => {\n let current: NodePath | null = replacement.path;\n while (current) {\n if (current.node === componentPath.node) return true;\n\n current = current.parentPath;\n }\n return false;\n });\n\n if (hasReplacements) {\n const key = componentKeyMap.get(componentPath.node)!;\n let ancestorProvidesKey = false;\n let currentPath: NodePath | null = componentPath.parentPath;\n while (currentPath) {\n const ancestorPath = componentPaths.find(\n (path) => path.node === currentPath?.node\n );\n\n if (ancestorPath && !ancestorPath.isProgram()) {\n const ancestorKey = componentKeyMap.get(ancestorPath.node);\n\n if (ancestorKey === key) {\n const ancestorHasReplacements = replacements.some((replacement) => {\n let rPath: NodePath | null = replacement.path;\n while (rPath) {\n if (rPath.node === ancestorPath.node) return true;\n\n rPath = rPath.parentPath;\n }\n return false;\n });\n const existingInfo = getExistingIntlayerInfo(ancestorPath);\n\n if (ancestorHasReplacements || existingInfo) {\n ancestorProvidesKey = true;\n break;\n }\n }\n }\n currentPath = currentPath.parentPath;\n }\n\n if (!ancestorProvidesKey) {\n componentsNeedingHooks.add(componentPath);\n }\n }\n }\n\n return {\n extractedContent,\n replacements,\n componentsNeedingHooks,\n componentKeyMap,\n componentPaths,\n hookMap,\n isSolid: false,\n };\n};\n\n/**\n * High-level function to extract content from TS/JS/JSX/TSX AST.\n */\nexport const extractTsContent = (\n ast: t.File,\n fileCode: string,\n existingKeys: Set<string>,\n configuration: IntlayerConfig,\n filePath: string,\n unmergedDictionaries: Record<string, unknown> = {}\n): {\n extractedContent: Record<string, string>;\n replacements: BabelReplacement[];\n} => {\n const { extractedContent, replacements } = extractBabelContentForComponents(\n ast,\n fileCode,\n existingKeys,\n 'default',\n configuration,\n filePath,\n unmergedDictionaries\n );\n\n const flatContent: Record<string, string> = {};\n for (const group of Object.values(extractedContent)) {\n Object.assign(flatContent, group);\n }\n\n return { extractedContent: flatContent, replacements };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"babelProcessor.cjs","names":["_traverse","t","shouldExtract","getOrGenerateKey","getExistingIntlayerInfo","resolveDictionaryKey","getComponentName","ATTRIBUTES_TO_EXTRACT"],"sources":["../../../src/extractContent/babelProcessor.ts"],"sourcesContent":["import _traverse, { type NodePath } from '@babel/traverse';\nimport * as t from '@babel/types';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { resolveDictionaryKey } from '../extractContent/utils';\nimport {\n ATTRIBUTES_TO_EXTRACT,\n getComponentName,\n getExistingIntlayerInfo,\n getOrGenerateKey,\n shouldExtract,\n} from './utils';\n\nexport type BabelReplacement = {\n path: NodePath;\n key: string;\n type:\n | 'jsx-text'\n | 'jsx-attribute'\n | 'string-literal'\n | 'jsx-insertion'\n | 'jsx-text-combined'\n | 'template-literal';\n componentKey: string;\n childrenToReplace?: t.Node[];\n variables?: string[];\n};\n\n// CJS/ESM interop: @babel/traverse exports its function as `.default` in CJS bundles\nconst traverse = (\n typeof _traverse === 'function' ? _traverse : (_traverse as any).default\n) as typeof _traverse;\n\n/**\n * Handles JSX insertions (elements with multiple children, including expressions).\n * Replaces complex JSX structures with variable-based translations.\n */\nexport const handleJsxInsertionBabel = (\n path: NodePath<t.JSXElement | t.JSXFragment>,\n fileCode: string,\n existingKeys: Set<string>,\n getComponentKeyForPath: (path: NodePath) => string,\n extractedContent: Record<string, Record<string, string>>,\n replacements: BabelReplacement[],\n handledNodes: Set<t.Node>\n): boolean => {\n const children = path.node.children;\n\n if (children.length <= 1) return false;\n\n const parts: {\n type: 'text' | 'var';\n value: string;\n originalExpr?: string;\n }[] = [];\n let hasSignificantText = false;\n let hasVariables = false;\n\n for (const child of children) {\n if (t.isJSXText(child)) {\n const text = child.value;\n\n if (text.trim().length > 0) hasSignificantText = true;\n\n parts.push({ type: 'text', value: text });\n } else if (t.isJSXExpressionContainer(child)) {\n if (t.isJSXEmptyExpression(child.expression)) {\n parts.push({ type: 'text', value: '' });\n } else {\n const expr = child.expression;\n\n if (t.isIdentifier(expr)) {\n parts.push({\n type: 'var',\n value: expr.name,\n originalExpr: expr.name,\n });\n hasVariables = true;\n } else if (t.isMemberExpression(expr)) {\n const code = fileCode.substring(expr.start!, expr.end!);\n\n const varName = t.isIdentifier(expr.property)\n ? expr.property.name\n : 'var';\n\n parts.push({ type: 'var', value: varName, originalExpr: code });\n\n hasVariables = true;\n } else if (t.isTemplateLiteral(expr)) {\n for (let i = 0; i < expr.quasis.length; i++) {\n parts.push({ type: 'text', value: expr.quasis[i].value.raw });\n if (i < expr.expressions.length) {\n const subExpr = expr.expressions[i];\n if (t.isIdentifier(subExpr)) {\n parts.push({\n type: 'var',\n value: subExpr.name,\n originalExpr: subExpr.name,\n });\n hasVariables = true;\n } else if (t.isMemberExpression(subExpr)) {\n const code = fileCode.substring(subExpr.start!, subExpr.end!);\n const varName = t.isIdentifier(subExpr.property)\n ? subExpr.property.name\n : 'var';\n parts.push({ type: 'var', value: varName, originalExpr: code });\n hasVariables = true;\n } else {\n return false;\n }\n }\n }\n } else {\n return false;\n }\n }\n } else {\n return false;\n }\n }\n\n if (!hasSignificantText) return false;\n\n let combinedString = '';\n for (const part of parts) {\n if (part.type === 'var') combinedString += `{{${part.value}}}`;\n else combinedString += part.value;\n }\n\n const cleanString = combinedString.replace(/\\s+/g, ' ').trim();\n\n if (shouldExtract(cleanString)) {\n const componentKey = getComponentKeyForPath(path);\n const key = getOrGenerateKey(\n cleanString,\n componentKey,\n existingKeys,\n extractedContent\n );\n\n const varMap = parts\n .filter((part) => part.type === 'var')\n .map((part) => `${part.value}: ${part.originalExpr}`);\n const uniqueVars = Array.from(new Set(varMap));\n\n if (hasVariables) {\n replacements.push({\n path,\n key,\n type: 'jsx-insertion',\n componentKey,\n childrenToReplace: children,\n variables: uniqueVars,\n });\n } else {\n replacements.push({\n path,\n key,\n type: 'jsx-text-combined',\n componentKey,\n childrenToReplace: children,\n });\n }\n\n children.forEach((child) => {\n handledNodes.add(child);\n });\n return true;\n }\n\n return false;\n};\n\n/**\n * Traverses the AST to identify components and extract content.\n * Returns extraction results and metadata about which components need Intlayer hooks.\n */\nexport const extractBabelContentForComponents = (\n ast: t.File,\n fileCode: string,\n existingKeys: Set<string>,\n defaultKey: string = 'default',\n configuration: IntlayerConfig,\n filePath: string,\n unmergedDictionaries: Record<string, unknown> = {}\n): {\n extractedContent: Record<string, Record<string, string>>;\n replacements: BabelReplacement[];\n componentsNeedingHooks: Set<NodePath>;\n componentKeyMap: Map<t.Node, string>;\n componentPaths: NodePath[];\n hookMap: Map<t.Node, 'useIntlayer' | 'getIntlayer'>;\n isSolid: boolean;\n} => {\n const extractedContent: Record<string, Record<string, string>> = {};\n const replacements: BabelReplacement[] = [];\n const handledNodes = new Set<t.Node>();\n const componentKeyMap = new Map<t.Node, string>();\n const hookMap = new Map<t.Node, 'useIntlayer' | 'getIntlayer'>();\n const usedKeysInFile = new Set<string>();\n let globalFileKey: string | undefined;\n\n const componentPaths: NodePath[] = [];\n\n traverse(ast, {\n Program(path) {\n componentPaths.push(path);\n },\n FunctionDeclaration(path) {\n componentPaths.push(path);\n },\n ArrowFunctionExpression(path) {\n componentPaths.push(path);\n },\n FunctionExpression(path) {\n componentPaths.push(path);\n },\n });\n\n // Pre-scan non-Program paths to collect their existing dictionary keys before the\n // Program scope is assigned. Without this, Program is processed first (depth-first\n // traversal) and creates a new file-path-derived key even when a child component\n // already declares a specific dictionary (e.g. useIntlayer('dashboard-sidebar')).\n for (const path of componentPaths) {\n if (path.isProgram()) continue;\n const existingInfo = getExistingIntlayerInfo(path);\n if (existingInfo) {\n usedKeysInFile.add(existingInfo.key);\n }\n }\n\n for (const path of componentPaths) {\n const existingInfo = getExistingIntlayerInfo(path);\n\n if (existingInfo) {\n componentKeyMap.set(path.node, existingInfo.key);\n usedKeysInFile.add(existingInfo.key);\n hookMap.set(path.node, existingInfo.hook);\n } else {\n if (path.isProgram()) {\n if (!globalFileKey) {\n // Reuse the dominant existing key from child components so that\n // module-level strings join the same dictionary rather than\n // creating a new file-path-derived one (e.g. 'route').\n const dominantKey =\n usedKeysInFile.size > 0 ? [...usedKeysInFile][0] : undefined;\n if (dominantKey) {\n globalFileKey = dominantKey;\n } else {\n globalFileKey = resolveDictionaryKey(\n defaultKey,\n filePath,\n configuration,\n unmergedDictionaries,\n usedKeysInFile\n );\n usedKeysInFile.add(globalFileKey);\n }\n }\n componentKeyMap.set(path.node, globalFileKey);\n hookMap.set(path.node, 'getIntlayer');\n } else {\n let inheritedKey: string | undefined;\n let parent: NodePath | null = path.parentPath;\n while (parent) {\n if (componentKeyMap.has(parent.node)) {\n inheritedKey = componentKeyMap.get(parent.node);\n break;\n }\n parent = parent.parentPath;\n }\n\n if (!inheritedKey) {\n if (!globalFileKey) {\n globalFileKey = resolveDictionaryKey(\n defaultKey,\n filePath,\n configuration,\n unmergedDictionaries,\n usedKeysInFile\n );\n usedKeysInFile.add(globalFileKey);\n }\n inheritedKey = globalFileKey;\n }\n\n componentKeyMap.set(path.node, inheritedKey);\n\n const compName = getComponentName(path);\n const isComponent = compName ? /^[A-Z]/.test(compName) : false;\n const isHook = compName ? /^use[A-Z]/.test(compName) : false;\n hookMap.set(\n path.node,\n isComponent || isHook ? 'useIntlayer' : 'getIntlayer'\n );\n }\n }\n }\n\n const getComponentKeyForPath = (path: NodePath): string => {\n let current: NodePath | null = path;\n while (current) {\n if (componentKeyMap.has(current.node)) {\n return componentKeyMap.get(current.node)!;\n }\n current = current.parentPath;\n }\n return globalFileKey || defaultKey;\n };\n\n traverse(ast, {\n JSXElement(path) {\n if (handledNodes.has(path.node)) return;\n\n handleJsxInsertionBabel(\n path,\n fileCode,\n existingKeys,\n getComponentKeyForPath,\n extractedContent,\n replacements,\n handledNodes\n );\n },\n JSXFragment(path) {\n if (handledNodes.has(path.node)) return;\n\n handleJsxInsertionBabel(\n path,\n fileCode,\n existingKeys,\n getComponentKeyForPath,\n extractedContent,\n replacements,\n handledNodes\n );\n },\n JSXText(path) {\n if (handledNodes.has(path.node)) return;\n\n const text = path.node.value;\n\n if (shouldExtract(text)) {\n const componentKey = getComponentKeyForPath(path);\n const key = getOrGenerateKey(\n text.replace(/\\s+/g, ' ').trim(),\n componentKey,\n existingKeys,\n extractedContent\n );\n replacements.push({ path, key, type: 'jsx-text', componentKey });\n }\n },\n JSXAttribute(path) {\n if (handledNodes.has(path.node)) return;\n\n const name = path.node.name.name;\n\n if (\n typeof name !== 'string' ||\n !ATTRIBUTES_TO_EXTRACT.includes(name as any)\n )\n return;\n const value = path.node.value;\n\n if (t.isStringLiteral(value) && shouldExtract(value.value)) {\n const componentKey = getComponentKeyForPath(path);\n const key = getOrGenerateKey(\n value.value.trim(),\n componentKey,\n existingKeys,\n extractedContent\n );\n replacements.push({ path, key, type: 'jsx-attribute', componentKey });\n }\n },\n StringLiteral(path) {\n if (handledNodes.has(path.node)) return;\n\n const text = path.node.value;\n\n if (!shouldExtract(text)) return;\n\n const parent = path.parentPath;\n\n if (\n parent.isImportDeclaration() ||\n parent.isImportSpecifier() ||\n parent.isExportDeclaration()\n )\n return;\n\n if (parent.isJSXAttribute()) return;\n\n if (\n parent.isCallExpression() &&\n t.isMemberExpression(parent.node.callee)\n ) {\n if (\n t.isIdentifier(parent.node.callee.object) &&\n parent.node.callee.object.name === 'console' &&\n t.isIdentifier(parent.node.callee.property) &&\n parent.node.callee.property.name === 'log'\n ) {\n return;\n }\n }\n\n if (parent.isObjectProperty() && parent.node.key === path.node) return;\n\n // Skip string values in named object properties (identifier key, e.g. `icon: 'Globe'`).\n // These are technical mappings, not translatable text. String-keyed properties\n // (e.g. `'translation-status': 'Translation Status'`) are still extracted.\n if (parent.isObjectProperty() && t.isIdentifier(parent.node.key)) return;\n\n if (parent.isMemberExpression() && parent.node.property === path.node)\n return;\n\n const componentKey = getComponentKeyForPath(path);\n const key = getOrGenerateKey(\n text.trim(),\n componentKey,\n existingKeys,\n extractedContent\n );\n replacements.push({ path, key, type: 'string-literal', componentKey });\n },\n TemplateLiteral(path) {\n if (handledNodes.has(path.node)) return;\n\n const { quasis, expressions } = path.node;\n\n // Build the combined string with placeholders\n let combinedString = '';\n const variables: string[] = [];\n let hasSignificantText = false;\n\n for (let i = 0; i < quasis.length; i++) {\n const text = quasis[i].value.raw;\n combinedString += text;\n if (text.trim().length > 0) hasSignificantText = true;\n\n if (i < expressions.length) {\n const expr = expressions[i];\n if (t.isIdentifier(expr)) {\n combinedString += `{{${expr.name}}}`;\n variables.push(`${expr.name}: ${expr.name}`);\n } else if (t.isMemberExpression(expr)) {\n const code = fileCode.substring(expr.start!, expr.end!);\n const varName = t.isIdentifier(expr.property)\n ? expr.property.name\n : 'var';\n combinedString += `{{${varName}}}`;\n variables.push(`${varName}: ${code}`);\n } else {\n // Complex expression in template literal, skip\n return;\n }\n }\n }\n\n if (!hasSignificantText) return;\n\n const cleanString = combinedString.replace(/\\s+/g, ' ').trim();\n\n if (!shouldExtract(cleanString)) return;\n\n const componentKey = getComponentKeyForPath(path);\n const key = getOrGenerateKey(\n cleanString,\n componentKey,\n existingKeys,\n extractedContent\n );\n\n const uniqueVars = Array.from(new Set(variables));\n\n replacements.push({\n path,\n key,\n type: 'template-literal',\n componentKey,\n variables: uniqueVars,\n });\n },\n });\n\n const componentsNeedingHooks = new Set<NodePath>();\n for (const componentPath of componentPaths) {\n if (componentPath.isProgram()) {\n const hasDirectReplacements = replacements.some((replacement) => {\n let current: NodePath | null = replacement.path;\n while (current) {\n if (current.node === componentPath.node) {\n return true;\n }\n const isOtherComponent = componentPaths.some(\n (p) => p !== componentPath && p.node === current?.node\n );\n if (isOtherComponent) {\n return false;\n }\n current = current.parentPath;\n }\n return false;\n });\n\n if (hasDirectReplacements) {\n componentsNeedingHooks.add(componentPath);\n }\n continue;\n }\n\n const hasReplacements = replacements.some((replacement) => {\n let current: NodePath | null = replacement.path;\n while (current) {\n if (current.node === componentPath.node) return true;\n\n current = current.parentPath;\n }\n return false;\n });\n\n if (hasReplacements) {\n const key = componentKeyMap.get(componentPath.node)!;\n let ancestorProvidesKey = false;\n let currentPath: NodePath | null = componentPath.parentPath;\n while (currentPath) {\n const ancestorPath = componentPaths.find(\n (path) => path.node === currentPath?.node\n );\n\n if (ancestorPath && !ancestorPath.isProgram()) {\n const ancestorKey = componentKeyMap.get(ancestorPath.node);\n\n if (ancestorKey === key) {\n const ancestorHasReplacements = replacements.some((replacement) => {\n let rPath: NodePath | null = replacement.path;\n while (rPath) {\n if (rPath.node === ancestorPath.node) return true;\n\n rPath = rPath.parentPath;\n }\n return false;\n });\n const existingInfo = getExistingIntlayerInfo(ancestorPath);\n\n if (ancestorHasReplacements || existingInfo) {\n ancestorProvidesKey = true;\n break;\n }\n }\n }\n currentPath = currentPath.parentPath;\n }\n\n if (!ancestorProvidesKey) {\n componentsNeedingHooks.add(componentPath);\n }\n }\n }\n\n return {\n extractedContent,\n replacements,\n componentsNeedingHooks,\n componentKeyMap,\n componentPaths,\n hookMap,\n isSolid: false,\n };\n};\n\n/**\n * High-level function to extract content from TS/JS/JSX/TSX AST.\n */\nexport const extractTsContent = (\n ast: t.File,\n fileCode: string,\n existingKeys: Set<string>,\n configuration: IntlayerConfig,\n filePath: string,\n unmergedDictionaries: Record<string, unknown> = {}\n): {\n extractedContent: Record<string, string>;\n replacements: BabelReplacement[];\n} => {\n const { extractedContent, replacements } = extractBabelContentForComponents(\n ast,\n fileCode,\n existingKeys,\n 'default',\n configuration,\n filePath,\n unmergedDictionaries\n );\n\n const flatContent: Record<string, string> = {};\n for (const group of Object.values(extractedContent)) {\n Object.assign(flatContent, group);\n }\n\n return { extractedContent: flatContent, replacements };\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contentWriter.cjs","names":["NodeTypes","resolveContentFilePaths"],"sources":["../../../src/extractContent/contentWriter.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { mkdir } from 'node:fs/promises';\nimport { dirname, relative } from 'node:path';\nimport {\n buildDictionary,\n ensureIntlayerBundle,\n loadContentDeclaration,\n writeContentDeclaration,\n} from '@intlayer/chokidar/build';\nimport { insertContentInDictionary } from '@intlayer/core/plugins';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Dictionary, DictionaryKey } from '@intlayer/types/dictionary';\nimport * as NodeTypes from '@intlayer/types/nodeType';\nimport { resolveContentFilePaths } from './utils/extractDictionaryInfo';\n\nconst hasInsertionVars = (str: string): boolean => /\\{\\{[^}]+\\}\\}/.test(str);\n\nconst getInsertionFields = (template: string): string[] => {\n const fields: string[] = [];\n const regex = /\\{\\{([^}]+)\\}\\}/g;\n let match: RegExpExecArray | null;\n for (\n let result = regex.exec(template);\n result !== null;\n result = regex.exec(template)\n ) {\n match = result;\n const field = match[1].trim();\n if (!fields.includes(field)) fields.push(field);\n }\n return fields;\n};\n\n/**\n * Translation node structure used in dictionaries\n */\ntype TranslationNode = {\n nodeType: 'translation';\n translation: Record<string, string>;\n};\n\n/**\n * Dictionary content structure - map of keys to translation nodes\n */\ntype DictionaryContentMap = Record<string, TranslationNode>;\n\n/**\n * Cached bundle file path to optimize performance\n */\nlet cachedBundleFilePath: string | undefined;\n\n/**\n * Merge extracted content with existing dictionary for multilingual format.\n * - Keys in extracted but not in existing: added with default locale only\n * - Keys in both: preserve existing translations, update default locale value\n * - Keys in existing but not in extracted: removed (no longer in source)\n */\nexport const mergeWithExistingMultilingualDictionary = (\n extractedContent: Record<string, string>,\n existingDictionary: Dictionary | null,\n defaultLocale: string\n): DictionaryContentMap => {\n const dictionary: Dictionary =\n existingDictionary ??\n ({\n key: '',\n content: {},\n filePath: '',\n } as Dictionary);\n\n const mergedDictionary = insertContentInDictionary(\n dictionary,\n extractedContent,\n defaultLocale as Locale\n );\n\n const mergedContent = mergedDictionary.content as DictionaryContentMap;\n\n // Pruning: remove keys not in extractedContent\n const finalContent: DictionaryContentMap = {};\n for (const key in extractedContent) {\n finalContent[key] = mergedContent[key];\n }\n\n // Promote any key whose source text contains {{vars}} to an insertion node\n for (const key in extractedContent) {\n const rawValue = extractedContent[key];\n if (typeof rawValue === 'string' && hasInsertionVars(rawValue)) {\n const node = finalContent[key] as any;\n if (node && node.nodeType === NodeTypes.TRANSLATION) {\n (finalContent as any)[key] = {\n nodeType: NodeTypes.INSERTION,\n [NodeTypes.INSERTION]: node,\n fields: getInsertionFields(rawValue),\n };\n }\n }\n }\n\n return finalContent;\n};\n\n/**\n * Merge extracted content with existing dictionary for per-locale format.\n * - Keys in extracted but not in existing: added\n * - Keys in both: update value\n * - Keys in existing but not in extracted: removed (no longer in source)\n */\nexport const mergeWithExistingPerLocaleDictionary = (\n extractedContent: Record<string, string>,\n existingDictionary: Dictionary | null\n): Record<string, string> => {\n const dictionary: Dictionary =\n existingDictionary ??\n ({\n key: '',\n content: {},\n filePath: '',\n } as Dictionary);\n\n const mergedDictionary = insertContentInDictionary(\n dictionary,\n extractedContent\n );\n\n const mergedContent = mergedDictionary.content as Record<string, string>;\n\n // Pruning: remove keys not in extractedContent\n const finalContent: Record<string, string> = {};\n for (const key in extractedContent) {\n finalContent[key] = mergedContent[key];\n }\n\n // Promote any key whose source text contains {{vars}} to an insertion node\n for (const key in extractedContent) {\n const rawValue = extractedContent[key];\n if (typeof rawValue === 'string' && hasInsertionVars(rawValue)) {\n const currentVal = finalContent[key];\n if (typeof currentVal === 'string') {\n (finalContent as any)[key] = {\n nodeType: NodeTypes.INSERTION,\n [NodeTypes.INSERTION]: currentVal,\n fields: getInsertionFields(rawValue),\n };\n }\n }\n }\n\n return finalContent;\n};\n\n/**\n * Helper to write extracted content to dictionary file(s).\n */\nexport const writeContentHelper = async (\n extractedContent: Record<string, string>,\n dictionaryKey: DictionaryKey,\n filePath: string,\n configuration: IntlayerConfig\n): Promise<string> => {\n const { absolutePath, isPerLocale } = await resolveContentFilePaths(\n filePath,\n dictionaryKey,\n configuration\n );\n\n const { defaultLocale } = configuration.internationalization;\n const { baseDir } = configuration.system;\n\n if (!cachedBundleFilePath) {\n cachedBundleFilePath = await ensureIntlayerBundle(configuration);\n }\n\n const outputDir = dirname(absolutePath);\n\n // Ensure output directory exists\n await mkdir(outputDir, { recursive: true });\n\n // Read existing dictionary to preserve translations and metadata\n let existingDictionary: Dictionary | null = null;\n\n if (existsSync(absolutePath)) {\n try {\n const dictionary = await loadContentDeclaration(\n absolutePath,\n configuration,\n cachedBundleFilePath\n );\n\n existingDictionary = dictionary ?? null;\n } catch (error) {\n console.error(error);\n }\n }\n\n const relativeContentFilePath = relative(baseDir, absolutePath);\n\n let mergedDictionary: Dictionary;\n\n if (isPerLocale) {\n // Per-locale format: simple string content for a single locale\n const mergedContent = mergeWithExistingPerLocaleDictionary(\n extractedContent,\n existingDictionary\n );\n\n mergedDictionary = {\n // Preserve existing metadata\n ...existingDictionary,\n key: dictionaryKey,\n content: mergedContent,\n locale: defaultLocale,\n filePath: relativeContentFilePath,\n };\n } else {\n // Multilingual format: content wrapped in translation nodes for multiple locales\n const mergedContent = mergeWithExistingMultilingualDictionary(\n extractedContent,\n existingDictionary,\n defaultLocale\n );\n\n mergedDictionary = {\n // Preserve existing metadata\n ...existingDictionary,\n key: dictionaryKey,\n content: mergedContent,\n filePath: relativeContentFilePath,\n };\n }\n\n const relativeDir = relative(baseDir, outputDir);\n\n const writeResult = await writeContentDeclaration(\n mergedDictionary,\n configuration,\n {\n newDictionariesPath: relativeDir,\n localeList: [defaultLocale],\n }\n );\n\n // Build the dictionary immediately\n const dictionaryToBuild: Dictionary = {\n ...mergedDictionary,\n filePath: relative(baseDir, writeResult?.path ?? absolutePath),\n };\n\n await buildDictionary([dictionaryToBuild], configuration);\n\n return absolutePath;\n};\n"],"mappings":";;;;;;;;;;;;AAgBA,MAAM,oBAAoB,QAAyB,gBAAgB,KAAK,
|
|
1
|
+
{"version":3,"file":"contentWriter.cjs","names":["NodeTypes","resolveContentFilePaths"],"sources":["../../../src/extractContent/contentWriter.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { mkdir } from 'node:fs/promises';\nimport { dirname, relative } from 'node:path';\nimport {\n buildDictionary,\n ensureIntlayerBundle,\n loadContentDeclaration,\n writeContentDeclaration,\n} from '@intlayer/chokidar/build';\nimport { insertContentInDictionary } from '@intlayer/core/plugins';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Dictionary, DictionaryKey } from '@intlayer/types/dictionary';\nimport * as NodeTypes from '@intlayer/types/nodeType';\nimport { resolveContentFilePaths } from './utils/extractDictionaryInfo';\n\nconst hasInsertionVars = (str: string): boolean => /\\{\\{[^}]+\\}\\}/.test(str);\n\nconst getInsertionFields = (template: string): string[] => {\n const fields: string[] = [];\n const regex = /\\{\\{([^}]+)\\}\\}/g;\n let match: RegExpExecArray | null;\n for (\n let result = regex.exec(template);\n result !== null;\n result = regex.exec(template)\n ) {\n match = result;\n const field = match[1].trim();\n if (!fields.includes(field)) fields.push(field);\n }\n return fields;\n};\n\n/**\n * Translation node structure used in dictionaries\n */\ntype TranslationNode = {\n nodeType: 'translation';\n translation: Record<string, string>;\n};\n\n/**\n * Dictionary content structure - map of keys to translation nodes\n */\ntype DictionaryContentMap = Record<string, TranslationNode>;\n\n/**\n * Cached bundle file path to optimize performance\n */\nlet cachedBundleFilePath: string | undefined;\n\n/**\n * Merge extracted content with existing dictionary for multilingual format.\n * - Keys in extracted but not in existing: added with default locale only\n * - Keys in both: preserve existing translations, update default locale value\n * - Keys in existing but not in extracted: removed (no longer in source)\n */\nexport const mergeWithExistingMultilingualDictionary = (\n extractedContent: Record<string, string>,\n existingDictionary: Dictionary | null,\n defaultLocale: string\n): DictionaryContentMap => {\n const dictionary: Dictionary =\n existingDictionary ??\n ({\n key: '',\n content: {},\n filePath: '',\n } as Dictionary);\n\n const mergedDictionary = insertContentInDictionary(\n dictionary,\n extractedContent,\n defaultLocale as Locale\n );\n\n const mergedContent = mergedDictionary.content as DictionaryContentMap;\n\n // Pruning: remove keys not in extractedContent\n const finalContent: DictionaryContentMap = {};\n for (const key in extractedContent) {\n finalContent[key] = mergedContent[key];\n }\n\n // Promote any key whose source text contains {{vars}} to an insertion node\n for (const key in extractedContent) {\n const rawValue = extractedContent[key];\n if (typeof rawValue === 'string' && hasInsertionVars(rawValue)) {\n const node = finalContent[key] as any;\n if (node && node.nodeType === NodeTypes.TRANSLATION) {\n (finalContent as any)[key] = {\n nodeType: NodeTypes.INSERTION,\n [NodeTypes.INSERTION]: node,\n fields: getInsertionFields(rawValue),\n };\n }\n }\n }\n\n return finalContent;\n};\n\n/**\n * Merge extracted content with existing dictionary for per-locale format.\n * - Keys in extracted but not in existing: added\n * - Keys in both: update value\n * - Keys in existing but not in extracted: removed (no longer in source)\n */\nexport const mergeWithExistingPerLocaleDictionary = (\n extractedContent: Record<string, string>,\n existingDictionary: Dictionary | null\n): Record<string, string> => {\n const dictionary: Dictionary =\n existingDictionary ??\n ({\n key: '',\n content: {},\n filePath: '',\n } as Dictionary);\n\n const mergedDictionary = insertContentInDictionary(\n dictionary,\n extractedContent\n );\n\n const mergedContent = mergedDictionary.content as Record<string, string>;\n\n // Pruning: remove keys not in extractedContent\n const finalContent: Record<string, string> = {};\n for (const key in extractedContent) {\n finalContent[key] = mergedContent[key];\n }\n\n // Promote any key whose source text contains {{vars}} to an insertion node\n for (const key in extractedContent) {\n const rawValue = extractedContent[key];\n if (typeof rawValue === 'string' && hasInsertionVars(rawValue)) {\n const currentVal = finalContent[key];\n if (typeof currentVal === 'string') {\n (finalContent as any)[key] = {\n nodeType: NodeTypes.INSERTION,\n [NodeTypes.INSERTION]: currentVal,\n fields: getInsertionFields(rawValue),\n };\n }\n }\n }\n\n return finalContent;\n};\n\n/**\n * Helper to write extracted content to dictionary file(s).\n */\nexport const writeContentHelper = async (\n extractedContent: Record<string, string>,\n dictionaryKey: DictionaryKey,\n filePath: string,\n configuration: IntlayerConfig\n): Promise<string> => {\n const { absolutePath, isPerLocale } = await resolveContentFilePaths(\n filePath,\n dictionaryKey,\n configuration\n );\n\n const { defaultLocale } = configuration.internationalization;\n const { baseDir } = configuration.system;\n\n if (!cachedBundleFilePath) {\n cachedBundleFilePath = await ensureIntlayerBundle(configuration);\n }\n\n const outputDir = dirname(absolutePath);\n\n // Ensure output directory exists\n await mkdir(outputDir, { recursive: true });\n\n // Read existing dictionary to preserve translations and metadata\n let existingDictionary: Dictionary | null = null;\n\n if (existsSync(absolutePath)) {\n try {\n const dictionary = await loadContentDeclaration(\n absolutePath,\n configuration,\n cachedBundleFilePath\n );\n\n existingDictionary = dictionary ?? null;\n } catch (error) {\n console.error(error);\n }\n }\n\n const relativeContentFilePath = relative(baseDir, absolutePath);\n\n let mergedDictionary: Dictionary;\n\n if (isPerLocale) {\n // Per-locale format: simple string content for a single locale\n const mergedContent = mergeWithExistingPerLocaleDictionary(\n extractedContent,\n existingDictionary\n );\n\n mergedDictionary = {\n // Preserve existing metadata\n ...existingDictionary,\n key: dictionaryKey,\n content: mergedContent,\n locale: defaultLocale,\n filePath: relativeContentFilePath,\n };\n } else {\n // Multilingual format: content wrapped in translation nodes for multiple locales\n const mergedContent = mergeWithExistingMultilingualDictionary(\n extractedContent,\n existingDictionary,\n defaultLocale\n );\n\n mergedDictionary = {\n // Preserve existing metadata\n ...existingDictionary,\n key: dictionaryKey,\n content: mergedContent,\n filePath: relativeContentFilePath,\n };\n }\n\n const relativeDir = relative(baseDir, outputDir);\n\n const writeResult = await writeContentDeclaration(\n mergedDictionary,\n configuration,\n {\n newDictionariesPath: relativeDir,\n localeList: [defaultLocale],\n }\n );\n\n // Build the dictionary immediately\n const dictionaryToBuild: Dictionary = {\n ...mergedDictionary,\n filePath: relative(baseDir, writeResult?.path ?? absolutePath),\n };\n\n await buildDictionary([dictionaryToBuild], configuration);\n\n return absolutePath;\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extractContent.cjs","names":["extractDictionaryKey","ATTRIBUTES_TO_EXTRACT","extractTsContent","ANSIColors","processTsxFile","writeContentHelper"],"sources":["../../../src/extractContent/extractContent.ts"],"sourcesContent":["import { execSync } from 'node:child_process';\nimport { readFileSync, writeFileSync } from 'node:fs';\nimport { extname, relative } from 'node:path';\nimport type * as t from '@babel/types';\nimport { detectFormatCommand } from '@intlayer/chokidar/cli';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, colorizePath, getAppLogger } from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport { getProjectRequire } from '@intlayer/config/utils';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\nimport { extractTsContent } from './babelProcessor';\nimport { writeContentHelper } from './contentWriter';\nimport { processTsxFile } from './processTsxFile';\nimport {\n ATTRIBUTES_TO_EXTRACT,\n extractDictionaryKeyFromPath,\n type PackageName,\n shouldExtract,\n} from './utils';\nimport { extractDictionaryKey } from './utils/extractDictionaryKey';\nimport { generateKey } from './utils/generateKey';\n\nexport type ExtractIntlayerOptions = {\n configOptions?: GetConfigurationOptions;\n codeOnly?: boolean;\n declarationOnly?: boolean;\n unmergedDictionaries?: Record<string, unknown>;\n configuration?: IntlayerConfig;\n code?: string;\n onExtract?: (result: {\n key: string;\n content: Record<string, string>;\n filePath: string;\n }) => void | Promise<void>;\n};\n\ntype ExternalCompilerResult = {\n extractedContent: Record<string, string>;\n code: string;\n};\n\ntype ExternalCompilerOptions = {\n generateKey: typeof generateKey;\n shouldExtract: typeof shouldExtract;\n attributesToExtract: typeof ATTRIBUTES_TO_EXTRACT;\n extractDictionaryKeyFromPath: typeof extractDictionaryKeyFromPath;\n extractTsContent: (\n ast: unknown,\n code: string,\n keys: Set<string>,\n config: IntlayerConfig,\n path: string\n ) => ReturnType<typeof extractTsContent>;\n};\n\ntype VueCompiler = typeof import('@intlayer/vue-compiler');\ntype SvelteCompiler = typeof import('@intlayer/svelte-compiler');\n\n// Module caches\nlet vueCompiler: VueCompiler | null = null;\nlet svelteCompiler: SvelteCompiler | null = null;\n\ntype InternalExtractResult = {\n extractedContentMap: Record<string, Record<string, string>> | null;\n transformedCode: string | null;\n};\n\nconst formatCompilerResult = (\n componentKey: string,\n res?: ExternalCompilerResult\n): InternalExtractResult | undefined => {\n if (!res) return undefined;\n\n return {\n extractedContentMap: { [componentKey]: res.extractedContent },\n transformedCode: res.code,\n };\n};\n\ntype Dependencies = {\n vueCompiler: VueCompiler | null;\n svelteCompiler: SvelteCompiler | null;\n unmergedDictionaries: Record<string, unknown>;\n configuration: IntlayerConfig;\n};\n\nconst processFileInternal = (\n filePath: string,\n packageName: PackageName,\n options: ExtractIntlayerOptions | undefined,\n dependencies: Dependencies,\n saveComponent: boolean,\n providedComponentKey?: string\n): InternalExtractResult | undefined => {\n const fileText = options?.code ?? readFileSync(filePath, 'utf-8');\n const componentKey =\n providedComponentKey ??\n extractDictionaryKey(\n filePath,\n fileText,\n dependencies.configuration.compiler.dictionaryKeyPrefix\n );\n const ext = extname(filePath);\n\n const { vueCompiler, svelteCompiler, unmergedDictionaries, configuration } =\n dependencies;\n\n const compilerCommonOptions: ExternalCompilerOptions = {\n generateKey,\n shouldExtract,\n attributesToExtract: ATTRIBUTES_TO_EXTRACT,\n extractDictionaryKeyFromPath,\n extractTsContent: (ast, code, keys, config, path) =>\n extractTsContent(\n ast as t.File,\n code,\n keys,\n config,\n path,\n unmergedDictionaries\n ),\n };\n\n if (ext === '.vue') {\n if (!vueCompiler) {\n throw new Error(\n `Please install ${colorizePath('@intlayer/vue-compiler', ANSIColors.YELLOW)} to process Vue files.`\n );\n }\n\n const res = vueCompiler.processVueFile(\n filePath,\n componentKey,\n packageName,\n compilerCommonOptions,\n saveComponent,\n fileText\n );\n\n if (res) {\n return formatCompilerResult(componentKey, res);\n }\n }\n\n if (ext === '.svelte') {\n if (!svelteCompiler) {\n throw new Error(\n `Please install ${colorizePath('@intlayer/svelte-compiler', ANSIColors.YELLOW)} to process Svelte files.`\n );\n }\n\n const res = svelteCompiler.processSvelteFile(\n filePath,\n componentKey,\n packageName,\n compilerCommonOptions,\n saveComponent,\n fileText\n );\n\n if (res) {\n return formatCompilerResult(componentKey, res);\n }\n }\n\n if (ext === '.astro') {\n const frontmatterMatch = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---/.exec(fileText);\n let fakeTsxCode = fileText;\n let hasFrontmatter = false;\n\n if (frontmatterMatch) {\n hasFrontmatter = true;\n const matchStart = frontmatterMatch.index;\n const matchEnd = matchStart + frontmatterMatch[0].length;\n\n const before = fileText.substring(0, matchStart);\n const matchText = frontmatterMatch[0];\n const after = fileText.substring(matchEnd);\n\n const newMatchText = matchText\n .replace(/^---/, '///')\n .replace(/---$/, '///');\n fakeTsxCode = before + newMatchText + after;\n } else {\n // Add empty frontmatter with a dummy statement so imports get injected properly\n fakeTsxCode = `///\\n;\\n///\\n${fileText}`;\n }\n\n const result = processTsxFile(\n filePath,\n componentKey,\n packageName,\n configuration,\n false, // don't let processTsxFile write — we reconstruct the full file below\n unmergedDictionaries,\n fakeTsxCode\n );\n\n if (!result) return undefined;\n\n let modifiedFullCode = result.modifiedCode\n .replace(/^\\s*\\/\\/\\//, '---')\n .replace(/\\n\\/\\/\\//, '\\n---');\n\n if (!hasFrontmatter) {\n modifiedFullCode = modifiedFullCode.replace(/\\n;\\n---\\n/, '\\n---\\n');\n }\n\n if (saveComponent) {\n writeFileSync(filePath, modifiedFullCode);\n\n const formatCommand = detectFormatCommand(configuration);\n if (formatCommand) {\n try {\n execSync(formatCommand.replace('{{file}}', filePath), {\n stdio: 'inherit',\n cwd: dependencies.configuration.system.baseDir,\n });\n } catch (error) {\n console.error(error);\n }\n }\n }\n\n return {\n extractedContentMap: result.extractedContent,\n transformedCode: modifiedFullCode,\n };\n }\n\n if (['.tsx', '.jsx', '.ts', '.js', '.cjs', '.mjs'].includes(ext)) {\n const result = processTsxFile(\n filePath,\n componentKey,\n packageName,\n configuration,\n saveComponent,\n unmergedDictionaries,\n fileText\n );\n\n if (result) {\n return {\n extractedContentMap: result.extractedContent,\n transformedCode: result.modifiedCode,\n };\n }\n }\n\n return undefined;\n};\n\nconst handleExtractionSideEffects = async (\n extractedContentMap: Record<string, Record<string, string>>,\n filePath: string,\n options: ExtractIntlayerOptions | undefined,\n {\n configuration,\n baseDir,\n appLogger,\n }: {\n configuration: IntlayerConfig;\n baseDir: string;\n appLogger: ReturnType<typeof getAppLogger>;\n },\n saveComponent: boolean\n) => {\n if (options?.onExtract) {\n for (const [key, content] of Object.entries(extractedContentMap)) {\n await options.onExtract({ key, content, filePath });\n }\n }\n\n const writeContent = !options?.codeOnly && !options?.onExtract;\n\n if (writeContent) {\n for (const [key, content] of Object.entries(extractedContentMap)) {\n const contentFilePath = await writeContentHelper(\n content,\n key,\n filePath,\n configuration\n );\n\n const relativeContentFilePath = relative(baseDir, contentFilePath);\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} Created content file: ${colorizePath(relativeContentFilePath)}`\n );\n }\n }\n\n if (saveComponent) {\n const formatCommand = detectFormatCommand(configuration);\n\n if (formatCommand) {\n try {\n execSync(formatCommand.replace('{{file}}', filePath), {\n stdio: 'inherit',\n cwd: baseDir,\n });\n } catch (error) {\n console.error(error);\n }\n }\n\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} Updated component: ${colorizePath(relative(baseDir, filePath))}`\n );\n }\n};\n\ntype ExtractResult = {\n transformedCode: string | null;\n extractedContentMap: Record<string, Record<string, string>>;\n};\n\ntype ExtractContext = {\n configuration: IntlayerConfig;\n appLogger: ReturnType<typeof getAppLogger>;\n baseDir: string;\n unmergedDictionaries: Record<string, unknown>;\n saveComponent: boolean;\n componentExtension: string;\n};\n\nconst buildContext = (\n filePath: string,\n options: ExtractIntlayerOptions | undefined\n): ExtractContext => {\n const configuration =\n options?.configuration ?? getConfiguration(options?.configOptions);\n const appLogger = getAppLogger(configuration);\n const { baseDir } = configuration.system;\n const unmergedDictionaries =\n options?.unmergedDictionaries ?? getUnmergedDictionaries(configuration);\n const saveComponent = !options?.declarationOnly;\n const componentExtension = extname(filePath);\n\n return {\n configuration,\n appLogger,\n baseDir,\n unmergedDictionaries,\n saveComponent,\n componentExtension,\n };\n};\n\nexport const extractContent = async (\n filePath: string,\n packageName: PackageName,\n options?: ExtractIntlayerOptions\n): Promise<ExtractResult | undefined> => {\n const {\n configuration,\n appLogger,\n baseDir,\n unmergedDictionaries,\n saveComponent,\n componentExtension,\n } = buildContext(filePath, options);\n\n if (componentExtension === '.vue' && !vueCompiler) {\n try {\n vueCompiler = await import('@intlayer/vue-compiler');\n } catch {\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} Install ${colorizePath('@intlayer/vue-compiler', ANSIColors.YELLOW)} to process Vue files.`\n );\n }\n }\n\n if (componentExtension === '.svelte' && !svelteCompiler) {\n try {\n svelteCompiler = await import('@intlayer/svelte-compiler');\n } catch {\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} Install ${colorizePath('@intlayer/svelte-compiler', ANSIColors.YELLOW)} to process Svelte files.`\n );\n }\n }\n\n const fileText = options?.code ?? readFileSync(filePath, 'utf-8');\n const dictionaryKey = extractDictionaryKey(\n filePath,\n fileText,\n configuration.compiler.dictionaryKeyPrefix\n );\n\n const result = processFileInternal(\n filePath,\n packageName,\n options,\n { vueCompiler, svelteCompiler, unmergedDictionaries, configuration },\n saveComponent,\n dictionaryKey\n );\n\n if (!result?.extractedContentMap) {\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} No extractable text found in ${colorizePath(relative(baseDir, filePath))}`,\n { isVerbose: true }\n );\n return undefined;\n }\n\n await handleExtractionSideEffects(\n result.extractedContentMap,\n filePath,\n options,\n { configuration, baseDir, appLogger },\n saveComponent\n );\n\n return {\n transformedCode: result.transformedCode,\n extractedContentMap: result.extractedContentMap,\n };\n};\n\n/**\n * Synchronous variant of `extractContent` — used by the Babel plugin which\n * runs in a sync transform context (e.g. Next.js / Webpack).\n *\n * Differences from `extractContent`:\n * - Loads external compilers (Vue, Svelte) via `require()` instead of `import()`\n * - Fires `onExtract` callbacks as fire-and-forget (cannot await in sync context)\n * - Does NOT write dictionary files or run the code formatter; callers that\n * need persistence should supply an `onExtract` callback\n */\nexport const extractContentSync = (\n filePath: string,\n packageName: PackageName,\n options?: ExtractIntlayerOptions\n): ExtractResult | undefined => {\n const {\n configuration,\n appLogger,\n baseDir,\n unmergedDictionaries,\n saveComponent,\n componentExtension,\n } = buildContext(filePath, options);\n\n const requireFn = getProjectRequire();\n\n if (componentExtension === '.vue' && !vueCompiler) {\n try {\n vueCompiler = requireFn('@intlayer/vue-compiler') as VueCompiler;\n } catch {\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} Install ${colorizePath('@intlayer/vue-compiler', ANSIColors.YELLOW)} to process Vue files.`\n );\n }\n }\n\n if (componentExtension === '.svelte' && !svelteCompiler) {\n try {\n svelteCompiler = requireFn('@intlayer/svelte-compiler') as SvelteCompiler;\n } catch {\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} Install ${colorizePath('@intlayer/svelte-compiler', ANSIColors.YELLOW)} to process Svelte files.`\n );\n }\n }\n\n const result = processFileInternal(\n filePath,\n packageName,\n options,\n { vueCompiler, svelteCompiler, unmergedDictionaries, configuration },\n saveComponent\n );\n\n if (!result?.extractedContentMap) {\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} No extractable text found in ${colorizePath(relative(baseDir, filePath))}`,\n { isVerbose: true }\n );\n return undefined;\n }\n\n const { extractedContentMap, transformedCode } = result;\n\n if (options?.onExtract) {\n for (const [key, content] of Object.entries(extractedContentMap)) {\n void options.onExtract({ key, content, filePath });\n }\n }\n\n return { transformedCode, extractedContentMap };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"extractContent.cjs","names":["extractDictionaryKey","ATTRIBUTES_TO_EXTRACT","extractTsContent","ANSIColors","processTsxFile","writeContentHelper"],"sources":["../../../src/extractContent/extractContent.ts"],"sourcesContent":["import { execSync } from 'node:child_process';\nimport { readFileSync, writeFileSync } from 'node:fs';\nimport { extname, relative } from 'node:path';\nimport type * as t from '@babel/types';\nimport { detectFormatCommand } from '@intlayer/chokidar/cli';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, colorizePath, getAppLogger } from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport { getProjectRequire } from '@intlayer/config/utils';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\nimport { extractTsContent } from './babelProcessor';\nimport { writeContentHelper } from './contentWriter';\nimport { processTsxFile } from './processTsxFile';\nimport {\n ATTRIBUTES_TO_EXTRACT,\n extractDictionaryKeyFromPath,\n type PackageName,\n shouldExtract,\n} from './utils';\nimport { extractDictionaryKey } from './utils/extractDictionaryKey';\nimport { generateKey } from './utils/generateKey';\n\nexport type ExtractIntlayerOptions = {\n configOptions?: GetConfigurationOptions;\n codeOnly?: boolean;\n declarationOnly?: boolean;\n unmergedDictionaries?: Record<string, unknown>;\n configuration?: IntlayerConfig;\n code?: string;\n onExtract?: (result: {\n key: string;\n content: Record<string, string>;\n filePath: string;\n }) => void | Promise<void>;\n};\n\ntype ExternalCompilerResult = {\n extractedContent: Record<string, string>;\n code: string;\n};\n\ntype ExternalCompilerOptions = {\n generateKey: typeof generateKey;\n shouldExtract: typeof shouldExtract;\n attributesToExtract: typeof ATTRIBUTES_TO_EXTRACT;\n extractDictionaryKeyFromPath: typeof extractDictionaryKeyFromPath;\n extractTsContent: (\n ast: unknown,\n code: string,\n keys: Set<string>,\n config: IntlayerConfig,\n path: string\n ) => ReturnType<typeof extractTsContent>;\n};\n\ntype VueCompiler = typeof import('@intlayer/vue-compiler');\ntype SvelteCompiler = typeof import('@intlayer/svelte-compiler');\n\n// Module caches\nlet vueCompiler: VueCompiler | null = null;\nlet svelteCompiler: SvelteCompiler | null = null;\n\ntype InternalExtractResult = {\n extractedContentMap: Record<string, Record<string, string>> | null;\n transformedCode: string | null;\n};\n\nconst formatCompilerResult = (\n componentKey: string,\n res?: ExternalCompilerResult\n): InternalExtractResult | undefined => {\n if (!res) return undefined;\n\n return {\n extractedContentMap: { [componentKey]: res.extractedContent },\n transformedCode: res.code,\n };\n};\n\ntype Dependencies = {\n vueCompiler: VueCompiler | null;\n svelteCompiler: SvelteCompiler | null;\n unmergedDictionaries: Record<string, unknown>;\n configuration: IntlayerConfig;\n};\n\nconst processFileInternal = (\n filePath: string,\n packageName: PackageName,\n options: ExtractIntlayerOptions | undefined,\n dependencies: Dependencies,\n saveComponent: boolean,\n providedComponentKey?: string\n): InternalExtractResult | undefined => {\n const fileText = options?.code ?? readFileSync(filePath, 'utf-8');\n const componentKey =\n providedComponentKey ??\n extractDictionaryKey(\n filePath,\n fileText,\n dependencies.configuration.compiler.dictionaryKeyPrefix\n );\n const ext = extname(filePath);\n\n const { vueCompiler, svelteCompiler, unmergedDictionaries, configuration } =\n dependencies;\n\n const compilerCommonOptions: ExternalCompilerOptions = {\n generateKey,\n shouldExtract,\n attributesToExtract: ATTRIBUTES_TO_EXTRACT,\n extractDictionaryKeyFromPath,\n extractTsContent: (ast, code, keys, config, path) =>\n extractTsContent(\n ast as t.File,\n code,\n keys,\n config,\n path,\n unmergedDictionaries\n ),\n };\n\n if (ext === '.vue') {\n if (!vueCompiler) {\n throw new Error(\n `Please install ${colorizePath('@intlayer/vue-compiler', ANSIColors.YELLOW)} to process Vue files.`\n );\n }\n\n const res = vueCompiler.processVueFile(\n filePath,\n componentKey,\n packageName,\n compilerCommonOptions,\n saveComponent,\n fileText\n );\n\n if (res) {\n return formatCompilerResult(componentKey, res);\n }\n }\n\n if (ext === '.svelte') {\n if (!svelteCompiler) {\n throw new Error(\n `Please install ${colorizePath('@intlayer/svelte-compiler', ANSIColors.YELLOW)} to process Svelte files.`\n );\n }\n\n const res = svelteCompiler.processSvelteFile(\n filePath,\n componentKey,\n packageName,\n compilerCommonOptions,\n saveComponent,\n fileText\n );\n\n if (res) {\n return formatCompilerResult(componentKey, res);\n }\n }\n\n if (ext === '.astro') {\n const frontmatterMatch = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---/.exec(fileText);\n let fakeTsxCode = fileText;\n let hasFrontmatter = false;\n\n if (frontmatterMatch) {\n hasFrontmatter = true;\n const matchStart = frontmatterMatch.index;\n const matchEnd = matchStart + frontmatterMatch[0].length;\n\n const before = fileText.substring(0, matchStart);\n const matchText = frontmatterMatch[0];\n const after = fileText.substring(matchEnd);\n\n const newMatchText = matchText\n .replace(/^---/, '///')\n .replace(/---$/, '///');\n fakeTsxCode = before + newMatchText + after;\n } else {\n // Add empty frontmatter with a dummy statement so imports get injected properly\n fakeTsxCode = `///\\n;\\n///\\n${fileText}`;\n }\n\n const result = processTsxFile(\n filePath,\n componentKey,\n packageName,\n configuration,\n false, // don't let processTsxFile write — we reconstruct the full file below\n unmergedDictionaries,\n fakeTsxCode\n );\n\n if (!result) return undefined;\n\n let modifiedFullCode = result.modifiedCode\n .replace(/^\\s*\\/\\/\\//, '---')\n .replace(/\\n\\/\\/\\//, '\\n---');\n\n if (!hasFrontmatter) {\n modifiedFullCode = modifiedFullCode.replace(/\\n;\\n---\\n/, '\\n---\\n');\n }\n\n if (saveComponent) {\n writeFileSync(filePath, modifiedFullCode);\n\n const formatCommand = detectFormatCommand(configuration);\n if (formatCommand) {\n try {\n execSync(formatCommand.replace('{{file}}', filePath), {\n stdio: 'inherit',\n cwd: dependencies.configuration.system.baseDir,\n });\n } catch (error) {\n console.error(error);\n }\n }\n }\n\n return {\n extractedContentMap: result.extractedContent,\n transformedCode: modifiedFullCode,\n };\n }\n\n if (['.tsx', '.jsx', '.ts', '.js', '.cjs', '.mjs'].includes(ext)) {\n const result = processTsxFile(\n filePath,\n componentKey,\n packageName,\n configuration,\n saveComponent,\n unmergedDictionaries,\n fileText\n );\n\n if (result) {\n return {\n extractedContentMap: result.extractedContent,\n transformedCode: result.modifiedCode,\n };\n }\n }\n\n return undefined;\n};\n\nconst handleExtractionSideEffects = async (\n extractedContentMap: Record<string, Record<string, string>>,\n filePath: string,\n options: ExtractIntlayerOptions | undefined,\n {\n configuration,\n baseDir,\n appLogger,\n }: {\n configuration: IntlayerConfig;\n baseDir: string;\n appLogger: ReturnType<typeof getAppLogger>;\n },\n saveComponent: boolean\n) => {\n if (options?.onExtract) {\n for (const [key, content] of Object.entries(extractedContentMap)) {\n await options.onExtract({ key, content, filePath });\n }\n }\n\n const writeContent = !options?.codeOnly && !options?.onExtract;\n\n if (writeContent) {\n for (const [key, content] of Object.entries(extractedContentMap)) {\n const contentFilePath = await writeContentHelper(\n content,\n key,\n filePath,\n configuration\n );\n\n const relativeContentFilePath = relative(baseDir, contentFilePath);\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} Created content file: ${colorizePath(relativeContentFilePath)}`\n );\n }\n }\n\n if (saveComponent) {\n const formatCommand = detectFormatCommand(configuration);\n\n if (formatCommand) {\n try {\n execSync(formatCommand.replace('{{file}}', filePath), {\n stdio: 'inherit',\n cwd: baseDir,\n });\n } catch (error) {\n console.error(error);\n }\n }\n\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} Updated component: ${colorizePath(relative(baseDir, filePath))}`\n );\n }\n};\n\ntype ExtractResult = {\n transformedCode: string | null;\n extractedContentMap: Record<string, Record<string, string>>;\n};\n\ntype ExtractContext = {\n configuration: IntlayerConfig;\n appLogger: ReturnType<typeof getAppLogger>;\n baseDir: string;\n unmergedDictionaries: Record<string, unknown>;\n saveComponent: boolean;\n componentExtension: string;\n};\n\nconst buildContext = (\n filePath: string,\n options: ExtractIntlayerOptions | undefined\n): ExtractContext => {\n const configuration =\n options?.configuration ?? getConfiguration(options?.configOptions);\n const appLogger = getAppLogger(configuration);\n const { baseDir } = configuration.system;\n const unmergedDictionaries =\n options?.unmergedDictionaries ?? getUnmergedDictionaries(configuration);\n const saveComponent = !options?.declarationOnly;\n const componentExtension = extname(filePath);\n\n return {\n configuration,\n appLogger,\n baseDir,\n unmergedDictionaries,\n saveComponent,\n componentExtension,\n };\n};\n\nexport const extractContent = async (\n filePath: string,\n packageName: PackageName,\n options?: ExtractIntlayerOptions\n): Promise<ExtractResult | undefined> => {\n const {\n configuration,\n appLogger,\n baseDir,\n unmergedDictionaries,\n saveComponent,\n componentExtension,\n } = buildContext(filePath, options);\n\n if (componentExtension === '.vue' && !vueCompiler) {\n try {\n vueCompiler = await import('@intlayer/vue-compiler');\n } catch {\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} Install ${colorizePath('@intlayer/vue-compiler', ANSIColors.YELLOW)} to process Vue files.`\n );\n }\n }\n\n if (componentExtension === '.svelte' && !svelteCompiler) {\n try {\n svelteCompiler = await import('@intlayer/svelte-compiler');\n } catch {\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} Install ${colorizePath('@intlayer/svelte-compiler', ANSIColors.YELLOW)} to process Svelte files.`\n );\n }\n }\n\n const fileText = options?.code ?? readFileSync(filePath, 'utf-8');\n const dictionaryKey = extractDictionaryKey(\n filePath,\n fileText,\n configuration.compiler.dictionaryKeyPrefix\n );\n\n const result = processFileInternal(\n filePath,\n packageName,\n options,\n { vueCompiler, svelteCompiler, unmergedDictionaries, configuration },\n saveComponent,\n dictionaryKey\n );\n\n if (!result?.extractedContentMap) {\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} No extractable text found in ${colorizePath(relative(baseDir, filePath))}`,\n { isVerbose: true }\n );\n return undefined;\n }\n\n await handleExtractionSideEffects(\n result.extractedContentMap,\n filePath,\n options,\n { configuration, baseDir, appLogger },\n saveComponent\n );\n\n return {\n transformedCode: result.transformedCode,\n extractedContentMap: result.extractedContentMap,\n };\n};\n\n/**\n * Synchronous variant of `extractContent` — used by the Babel plugin which\n * runs in a sync transform context (e.g. Next.js / Webpack).\n *\n * Differences from `extractContent`:\n * - Loads external compilers (Vue, Svelte) via `require()` instead of `import()`\n * - Fires `onExtract` callbacks as fire-and-forget (cannot await in sync context)\n * - Does NOT write dictionary files or run the code formatter; callers that\n * need persistence should supply an `onExtract` callback\n */\nexport const extractContentSync = (\n filePath: string,\n packageName: PackageName,\n options?: ExtractIntlayerOptions\n): ExtractResult | undefined => {\n const {\n configuration,\n appLogger,\n baseDir,\n unmergedDictionaries,\n saveComponent,\n componentExtension,\n } = buildContext(filePath, options);\n\n const requireFn = getProjectRequire();\n\n if (componentExtension === '.vue' && !vueCompiler) {\n try {\n vueCompiler = requireFn('@intlayer/vue-compiler') as VueCompiler;\n } catch {\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} Install ${colorizePath('@intlayer/vue-compiler', ANSIColors.YELLOW)} to process Vue files.`\n );\n }\n }\n\n if (componentExtension === '.svelte' && !svelteCompiler) {\n try {\n svelteCompiler = requireFn('@intlayer/svelte-compiler') as SvelteCompiler;\n } catch {\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} Install ${colorizePath('@intlayer/svelte-compiler', ANSIColors.YELLOW)} to process Svelte files.`\n );\n }\n }\n\n const result = processFileInternal(\n filePath,\n packageName,\n options,\n { vueCompiler, svelteCompiler, unmergedDictionaries, configuration },\n saveComponent\n );\n\n if (!result?.extractedContentMap) {\n appLogger(\n `${colorize('Compiler:', ANSIColors.GREY_DARK)} No extractable text found in ${colorizePath(relative(baseDir, filePath))}`,\n { isVerbose: true }\n );\n return undefined;\n }\n\n const { extractedContentMap, transformedCode } = result;\n\n if (options?.onExtract) {\n for (const [key, content] of Object.entries(extractedContentMap)) {\n void options.onExtract({ key, content, filePath });\n }\n }\n\n return { transformedCode, extractedContentMap };\n};\n"],"mappings":"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"}
|
|
@@ -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\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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.cjs","names":[],"sources":["../../../../src/extractContent/utils/constants.ts"],"sourcesContent":["/**\n * Attributes that should be extracted as translatable strings from JSX/HTML elements.\n * This is the single source of truth shared across all Intlayer compiler packages\n * (@intlayer/babel, @intlayer/vue-compiler, @intlayer/svelte-compiler, @intlayer/chokidar).\n */\nexport const ATTRIBUTES_TO_EXTRACT = [\n 'title',\n 'placeholder',\n 'alt',\n 'aria-label',\n 'label',\n] as const;\n\n/**\n * The list of supported Intlayer integration packages.\n * This is the single source of truth for package name validation.\n *\n * Order matter for resolution\n */\nexport const packageList = [\n 'next-intlayer',\n 'react-intlayer',\n 'vue-intlayer',\n 'svelte-intlayer',\n 'preact-intlayer',\n 'solid-intlayer',\n 'angular-intlayer',\n 'lit-intlayer',\n 'express-intlayer',\n 'hono-intlayer',\n 'fastify-intlayer',\n 'adonis-intlayer',\n 'intlayer',\n] as const;\n\n/** Packages that support a `/server` sub-path for React Server Components. */\nexport const SERVER_CAPABLE_PACKAGES: ReadonlySet<string> = new Set([\n 'next-intlayer',\n]);\n\nexport type PackageName = (typeof packageList)[number];\n"],"mappings":";;;;;;;;AAKA,MAAa,wBAAwB;CACnC;CACA;CACA;CACA;CACA;
|
|
1
|
+
{"version":3,"file":"constants.cjs","names":[],"sources":["../../../../src/extractContent/utils/constants.ts"],"sourcesContent":["/**\n * Attributes that should be extracted as translatable strings from JSX/HTML elements.\n * This is the single source of truth shared across all Intlayer compiler packages\n * (@intlayer/babel, @intlayer/vue-compiler, @intlayer/svelte-compiler, @intlayer/chokidar).\n */\nexport const ATTRIBUTES_TO_EXTRACT = [\n 'title',\n 'placeholder',\n 'alt',\n 'aria-label',\n 'label',\n] as const;\n\n/**\n * The list of supported Intlayer integration packages.\n * This is the single source of truth for package name validation.\n *\n * Order matter for resolution\n */\nexport const packageList = [\n 'next-intlayer',\n 'react-intlayer',\n 'vue-intlayer',\n 'svelte-intlayer',\n 'preact-intlayer',\n 'solid-intlayer',\n 'angular-intlayer',\n 'lit-intlayer',\n 'express-intlayer',\n 'hono-intlayer',\n 'fastify-intlayer',\n 'adonis-intlayer',\n 'intlayer',\n] as const;\n\n/** Packages that support a `/server` sub-path for React Server Components. */\nexport const SERVER_CAPABLE_PACKAGES: ReadonlySet<string> = new Set([\n 'next-intlayer',\n]);\n\nexport type PackageName = (typeof packageList)[number];\n"],"mappings":";;;;;;;;AAKA,MAAa,wBAAwB;CACnC;CACA;CACA;CACA;CACA;AACF;;;;;;;AAQA,MAAa,cAAc;CACzB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;AAGA,MAAa,0BAA+C,IAAI,IAAI,CAClE,eACF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detectPackageName.cjs","names":["packageList"],"sources":["../../../../src/extractContent/utils/detectPackageName.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { getPackageJsonPath } from '@intlayer/config/utils';\nimport { type PackageName, packageList } from './constants';\n\n/**\n * Detects which intlayer package is used in the project by reading package.json.\n */\nexport const detectPackageName = (searchDir: string): PackageName => {\n const { packageJsonPath } = getPackageJsonPath(searchDir);\n\n if (existsSync(packageJsonPath)) {\n try {\n const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));\n const allDeps = {\n ...pkg.dependencies,\n ...pkg.devDependencies,\n ...pkg.peerDependencies,\n };\n\n for (const pkgName of packageList) {\n if (allDeps[pkgName]) return pkgName;\n }\n } catch {\n // Ignore JSON errors\n }\n }\n\n return packageList[packageList.length - 1];\n};\n"],"mappings":";;;;;;;;;;AAOA,MAAa,qBAAqB,cAAmC;CACnE,MAAM,EAAE,mEAAuC,
|
|
1
|
+
{"version":3,"file":"detectPackageName.cjs","names":["packageList"],"sources":["../../../../src/extractContent/utils/detectPackageName.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { getPackageJsonPath } from '@intlayer/config/utils';\nimport { type PackageName, packageList } from './constants';\n\n/**\n * Detects which intlayer package is used in the project by reading package.json.\n */\nexport const detectPackageName = (searchDir: string): PackageName => {\n const { packageJsonPath } = getPackageJsonPath(searchDir);\n\n if (existsSync(packageJsonPath)) {\n try {\n const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));\n const allDeps = {\n ...pkg.dependencies,\n ...pkg.devDependencies,\n ...pkg.peerDependencies,\n };\n\n for (const pkgName of packageList) {\n if (allDeps[pkgName]) return pkgName;\n }\n } catch {\n // Ignore JSON errors\n }\n }\n\n return packageList[packageList.length - 1];\n};\n"],"mappings":";;;;;;;;;;AAOA,MAAa,qBAAqB,cAAmC;CACnE,MAAM,EAAE,mEAAuC,SAAS;CAExD,4BAAe,eAAe,GAC5B,IAAI;EACF,MAAM,MAAM,KAAK,gCAAmB,iBAAiB,OAAO,CAAC;EAC7D,MAAM,UAAU;GACd,GAAG,IAAI;GACP,GAAG,IAAI;GACP,GAAG,IAAI;EACT;EAEA,KAAK,MAAM,WAAWA,oDACpB,IAAI,QAAQ,UAAU,OAAO;CAEjC,QAAQ,CAER;CAGF,OAAOA,mDAAYA,mDAAY,SAAS;AAC1C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extractDictionaryInfo.cjs","names":["ANSIColors","extractDictionaryKey"],"sources":["../../../../src/extractContent/utils/extractDictionaryInfo.ts"],"sourcesContent":["import { basename, dirname, extname, relative, resolve } from 'node:path';\nimport {\n getFormatFromExtension,\n resolveRelativePath,\n} from '@intlayer/chokidar/utils';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize } from '@intlayer/config/logger';\nimport { assertPathWithin, parseStringPattern } from '@intlayer/config/utils';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Fill } from '@intlayer/types/dictionary';\nimport type {\n FilePathPattern,\n FilePathPatternContext,\n FilePathPatternFunction,\n} from '@intlayer/types/filePathPattern';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\nimport { extractDictionaryKey } from './extractDictionaryKey';\n\nexport const getOutput = (\n configuration: IntlayerConfig,\n locale?: Locale\n): FilePathPatternFunction | false => {\n const output = configuration.compiler?.output as Fill | undefined;\n\n if (output === undefined || output === null) {\n throw new Error(\n `No output configuration found. Add a ${colorize('compiler.output', ANSIColors.BLUE)} in your configuration.`\n );\n }\n\n if (output === false) return false;\n\n // Object per-locale record: look up the entry for the given locale\n if (typeof output === 'object' && typeof output !== 'function') {\n const entry = locale\n ? (output as Record<string, boolean | FilePathPattern>)[locale]\n : undefined;\n if (entry === false) return false;\n if (entry === undefined || entry === true) {\n throw new Error(\n `No output pattern configured for locale \"${locale}\" in compiler.output.`\n );\n }\n return typeof entry === 'string'\n ? (context: FilePathPatternContext) =>\n parseStringPattern(entry as string, context)\n : (entry as FilePathPatternFunction);\n }\n\n if (typeof output === 'string') {\n return (context: FilePathPatternContext) =>\n parseStringPattern(output, context);\n }\n\n if (typeof output === 'function') {\n return output as FilePathPatternFunction;\n }\n\n throw new Error(\n `No output configuration found. Add a ${colorize('compiler.output', ANSIColors.BLUE)} in your configuration.`\n );\n};\n\nexport type ResolveContentFilePaths = {\n absolutePath: string;\n relativePath: string;\n isPerLocale: boolean;\n};\n\n/**\n * Resolves the paths for the content files associated with a component.\n * Checks for existing dictionaries first.\n */\nexport const resolveContentFilePaths = async (\n filePath: string,\n componentKey: string,\n configuration: IntlayerConfig,\n locale?: Locale\n): Promise<ResolveContentFilePaths> => {\n const { baseDir } = configuration.system;\n const { defaultLocale } = configuration.internationalization;\n\n const unmergedDictionaries = getUnmergedDictionaries(configuration) ?? {};\n const existingDicts = unmergedDictionaries[componentKey]\n ?.filter(\n (dictionary) =>\n // Remove remote dictionaries (Fix: Check for !== instead of ===)\n dictionary.location !== 'remote'\n )\n .filter(\n (dictionary) =>\n // Check for first locale dictionary sorted by priority that include the targeted locale\n dictionary.locale === undefined ||\n dictionary.locale === (locale ?? defaultLocale)\n );\n\n if (existingDicts?.[0]?.filePath) {\n const existingPath = existingDicts[0].filePath;\n const resolvedAbsolutePath = resolve(baseDir, existingPath);\n\n assertPathWithin(resolvedAbsolutePath, baseDir);\n\n return {\n absolutePath: resolvedAbsolutePath,\n relativePath: relative(baseDir, resolvedAbsolutePath),\n isPerLocale: false,\n };\n }\n\n const output = configuration.compiler?.output as Fill | undefined;\n const isObjectOutput =\n typeof output === 'object' &&\n output !== null &&\n typeof output !== 'function';\n\n // Build shared context (used for both object and scalar output paths)\n const extension = extname(\n filePath\n ) as FilePathPatternContext['componentExtension'];\n const componentName = basename(filePath, extension);\n const uncapitalizedName =\n componentName.charAt(0).toLowerCase() + componentName.slice(1);\n const componentFormat = getFormatFromExtension(\n extension!\n ) as FilePathPatternContext['componentFormat'];\n\n const targetLocale = (locale ?? defaultLocale) as Locale;\n\n const context: FilePathPatternContext = {\n key: componentKey,\n componentDirPath: relative(baseDir, dirname(filePath)),\n componentFileName: componentName,\n fileName: uncapitalizedName,\n componentFormat,\n componentExtension: extension,\n format: componentFormat!,\n locale: targetLocale,\n extension: configuration.content.fileExtensions[0],\n };\n\n // Object output: each locale has its own pattern → always per-locale\n if (isObjectOutput) {\n const pattern = getOutput(configuration, targetLocale);\n if (pattern === false) {\n throw new Error(\n `compiler.output is disabled for locale \"${targetLocale}\".`\n );\n }\n const rawAbsolutePath = await pattern(context);\n const absolutePath = resolveRelativePath(\n rawAbsolutePath,\n filePath,\n baseDir\n );\n return {\n absolutePath,\n relativePath: relative(baseDir, absolutePath),\n isPerLocale: true,\n };\n }\n\n // Scalar string/function output: detect per-locale via dummy locale probe\n const pattern = getOutput(configuration);\n if (pattern === false) {\n throw new Error(\n `No output configuration found. Add a ${colorize('compiler.output', ANSIColors.BLUE)} in your configuration.`\n );\n }\n\n const rawAbsolutePath = await pattern({ ...context, locale: defaultLocale });\n\n // Apply the resolution rules\n const absolutePath = resolveRelativePath(rawAbsolutePath, filePath, baseDir);\n\n const localeIdentifier = '###########locale###########' as Locale;\n\n const rawPerLocalePath = await pattern({\n ...context,\n locale: localeIdentifier,\n });\n const isPerLocale = rawPerLocalePath.includes(localeIdentifier);\n\n return {\n absolutePath,\n relativePath: relative(baseDir, absolutePath),\n isPerLocale,\n };\n};\n\nexport type ExtractDictionaryInfoOptions = {\n configuration?: IntlayerConfig;\n};\n\n/**\n * Extracts the dictionary key and dictionary file path for a given component file.\n */\nexport const extractDictionaryInfo = async (\n filePath: string,\n fileText: string,\n configuration: IntlayerConfig\n): Promise<\n {\n dictionaryKey: string;\n } & ResolveContentFilePaths\n> => {\n const dictionaryKey = extractDictionaryKey(filePath, fileText);\n\n const resolvedPaths = await resolveContentFilePaths(\n filePath,\n dictionaryKey,\n configuration\n );\n\n return {\n dictionaryKey,\n ...resolvedPaths,\n };\n};\n"],"mappings":";;;;;;;;;;;;AAmBA,MAAa,aACX,eACA,WACoC;CACpC,MAAM,SAAS,cAAc,UAAU;CAEvC,IAAI,WAAW,UAAa,WAAW,MACrC,MAAM,IAAI,MACR,8EAAiD,mBAAmBA,wBAAW,
|
|
1
|
+
{"version":3,"file":"extractDictionaryInfo.cjs","names":["ANSIColors","extractDictionaryKey"],"sources":["../../../../src/extractContent/utils/extractDictionaryInfo.ts"],"sourcesContent":["import { basename, dirname, extname, relative, resolve } from 'node:path';\nimport {\n getFormatFromExtension,\n resolveRelativePath,\n} from '@intlayer/chokidar/utils';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize } from '@intlayer/config/logger';\nimport { assertPathWithin, parseStringPattern } from '@intlayer/config/utils';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Fill } from '@intlayer/types/dictionary';\nimport type {\n FilePathPattern,\n FilePathPatternContext,\n FilePathPatternFunction,\n} from '@intlayer/types/filePathPattern';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\nimport { extractDictionaryKey } from './extractDictionaryKey';\n\nexport const getOutput = (\n configuration: IntlayerConfig,\n locale?: Locale\n): FilePathPatternFunction | false => {\n const output = configuration.compiler?.output as Fill | undefined;\n\n if (output === undefined || output === null) {\n throw new Error(\n `No output configuration found. Add a ${colorize('compiler.output', ANSIColors.BLUE)} in your configuration.`\n );\n }\n\n if (output === false) return false;\n\n // Object per-locale record: look up the entry for the given locale\n if (typeof output === 'object' && typeof output !== 'function') {\n const entry = locale\n ? (output as Record<string, boolean | FilePathPattern>)[locale]\n : undefined;\n if (entry === false) return false;\n if (entry === undefined || entry === true) {\n throw new Error(\n `No output pattern configured for locale \"${locale}\" in compiler.output.`\n );\n }\n return typeof entry === 'string'\n ? (context: FilePathPatternContext) =>\n parseStringPattern(entry as string, context)\n : (entry as FilePathPatternFunction);\n }\n\n if (typeof output === 'string') {\n return (context: FilePathPatternContext) =>\n parseStringPattern(output, context);\n }\n\n if (typeof output === 'function') {\n return output as FilePathPatternFunction;\n }\n\n throw new Error(\n `No output configuration found. Add a ${colorize('compiler.output', ANSIColors.BLUE)} in your configuration.`\n );\n};\n\nexport type ResolveContentFilePaths = {\n absolutePath: string;\n relativePath: string;\n isPerLocale: boolean;\n};\n\n/**\n * Resolves the paths for the content files associated with a component.\n * Checks for existing dictionaries first.\n */\nexport const resolveContentFilePaths = async (\n filePath: string,\n componentKey: string,\n configuration: IntlayerConfig,\n locale?: Locale\n): Promise<ResolveContentFilePaths> => {\n const { baseDir } = configuration.system;\n const { defaultLocale } = configuration.internationalization;\n\n const unmergedDictionaries = getUnmergedDictionaries(configuration) ?? {};\n const existingDicts = unmergedDictionaries[componentKey]\n ?.filter(\n (dictionary) =>\n // Remove remote dictionaries (Fix: Check for !== instead of ===)\n dictionary.location !== 'remote'\n )\n .filter(\n (dictionary) =>\n // Check for first locale dictionary sorted by priority that include the targeted locale\n dictionary.locale === undefined ||\n dictionary.locale === (locale ?? defaultLocale)\n );\n\n if (existingDicts?.[0]?.filePath) {\n const existingPath = existingDicts[0].filePath;\n const resolvedAbsolutePath = resolve(baseDir, existingPath);\n\n assertPathWithin(resolvedAbsolutePath, baseDir);\n\n return {\n absolutePath: resolvedAbsolutePath,\n relativePath: relative(baseDir, resolvedAbsolutePath),\n isPerLocale: false,\n };\n }\n\n const output = configuration.compiler?.output as Fill | undefined;\n const isObjectOutput =\n typeof output === 'object' &&\n output !== null &&\n typeof output !== 'function';\n\n // Build shared context (used for both object and scalar output paths)\n const extension = extname(\n filePath\n ) as FilePathPatternContext['componentExtension'];\n const componentName = basename(filePath, extension);\n const uncapitalizedName =\n componentName.charAt(0).toLowerCase() + componentName.slice(1);\n const componentFormat = getFormatFromExtension(\n extension!\n ) as FilePathPatternContext['componentFormat'];\n\n const targetLocale = (locale ?? defaultLocale) as Locale;\n\n const context: FilePathPatternContext = {\n key: componentKey,\n componentDirPath: relative(baseDir, dirname(filePath)),\n componentFileName: componentName,\n fileName: uncapitalizedName,\n componentFormat,\n componentExtension: extension,\n format: componentFormat!,\n locale: targetLocale,\n extension: configuration.content.fileExtensions[0],\n };\n\n // Object output: each locale has its own pattern → always per-locale\n if (isObjectOutput) {\n const pattern = getOutput(configuration, targetLocale);\n if (pattern === false) {\n throw new Error(\n `compiler.output is disabled for locale \"${targetLocale}\".`\n );\n }\n const rawAbsolutePath = await pattern(context);\n const absolutePath = resolveRelativePath(\n rawAbsolutePath,\n filePath,\n baseDir\n );\n return {\n absolutePath,\n relativePath: relative(baseDir, absolutePath),\n isPerLocale: true,\n };\n }\n\n // Scalar string/function output: detect per-locale via dummy locale probe\n const pattern = getOutput(configuration);\n if (pattern === false) {\n throw new Error(\n `No output configuration found. Add a ${colorize('compiler.output', ANSIColors.BLUE)} in your configuration.`\n );\n }\n\n const rawAbsolutePath = await pattern({ ...context, locale: defaultLocale });\n\n // Apply the resolution rules\n const absolutePath = resolveRelativePath(rawAbsolutePath, filePath, baseDir);\n\n const localeIdentifier = '###########locale###########' as Locale;\n\n const rawPerLocalePath = await pattern({\n ...context,\n locale: localeIdentifier,\n });\n const isPerLocale = rawPerLocalePath.includes(localeIdentifier);\n\n return {\n absolutePath,\n relativePath: relative(baseDir, absolutePath),\n isPerLocale,\n };\n};\n\nexport type ExtractDictionaryInfoOptions = {\n configuration?: IntlayerConfig;\n};\n\n/**\n * Extracts the dictionary key and dictionary file path for a given component file.\n */\nexport const extractDictionaryInfo = async (\n filePath: string,\n fileText: string,\n configuration: IntlayerConfig\n): Promise<\n {\n dictionaryKey: string;\n } & ResolveContentFilePaths\n> => {\n const dictionaryKey = extractDictionaryKey(filePath, fileText);\n\n const resolvedPaths = await resolveContentFilePaths(\n filePath,\n dictionaryKey,\n configuration\n );\n\n return {\n dictionaryKey,\n ...resolvedPaths,\n };\n};\n"],"mappings":";;;;;;;;;;;;AAmBA,MAAa,aACX,eACA,WACoC;CACpC,MAAM,SAAS,cAAc,UAAU;CAEvC,IAAI,WAAW,UAAa,WAAW,MACrC,MAAM,IAAI,MACR,8EAAiD,mBAAmBA,wBAAW,IAAI,EAAE,wBACvF;CAGF,IAAI,WAAW,OAAO,OAAO;CAG7B,IAAI,OAAO,WAAW,YAAY,OAAO,WAAW,YAAY;EAC9D,MAAM,QAAQ,SACT,OAAqD,UACtD;EACJ,IAAI,UAAU,OAAO,OAAO;EAC5B,IAAI,UAAU,UAAa,UAAU,MACnC,MAAM,IAAI,MACR,4CAA4C,OAAO,sBACrD;EAEF,OAAO,OAAO,UAAU,YACnB,2DACoB,OAAiB,OAAO,IAC5C;CACP;CAEA,IAAI,OAAO,WAAW,UACpB,QAAQ,2DACa,QAAQ,OAAO;CAGtC,IAAI,OAAO,WAAW,YACpB,OAAO;CAGT,MAAM,IAAI,MACR,8EAAiD,mBAAmBA,wBAAW,IAAI,EAAE,wBACvF;AACF;;;;;AAYA,MAAa,0BAA0B,OACrC,UACA,cACA,eACA,WACqC;CACrC,MAAM,EAAE,YAAY,cAAc;CAClC,MAAM,EAAE,kBAAkB,cAAc;CAGxC,MAAM,oFAD+C,aAAa,KAAK,CAAC,GAC7B,eACvC,QACC,eAEC,WAAW,aAAa,QAC5B,EACC,QACE,eAEC,WAAW,WAAW,UACtB,WAAW,YAAY,UAAU,cACrC;CAEF,IAAI,gBAAgB,IAAI,UAAU;EAChC,MAAM,eAAe,cAAc,GAAG;EACtC,MAAM,8CAA+B,SAAS,YAAY;EAE1D,6CAAiB,sBAAsB,OAAO;EAE9C,OAAO;GACL,cAAc;GACd,sCAAuB,SAAS,oBAAoB;GACpD,aAAa;EACf;CACF;CAEA,MAAM,SAAS,cAAc,UAAU;CACvC,MAAM,iBACJ,OAAO,WAAW,YAClB,WAAW,QACX,OAAO,WAAW;CAGpB,MAAM,mCACJ,QACF;CACA,MAAM,wCAAyB,UAAU,SAAS;CAClD,MAAM,oBACJ,cAAc,OAAO,CAAC,EAAE,YAAY,IAAI,cAAc,MAAM,CAAC;CAC/D,MAAM,uEACJ,SACF;CAEA,MAAM,eAAgB,UAAU;CAEhC,MAAM,UAAkC;EACtC,KAAK;EACL,0CAA2B,gCAAiB,QAAQ,CAAC;EACrD,mBAAmB;EACnB,UAAU;EACV;EACA,oBAAoB;EACpB,QAAQ;EACR,QAAQ;EACR,WAAW,cAAc,QAAQ,eAAe;CAClD;CAGA,IAAI,gBAAgB;EAClB,MAAM,UAAU,UAAU,eAAe,YAAY;EACrD,IAAI,YAAY,OACd,MAAM,IAAI,MACR,2CAA2C,aAAa,GAC1D;EAGF,MAAM,iEACJ,MAF4B,QAAQ,OAAO,GAG3C,UACA,OACF;EACA,OAAO;GACL;GACA,sCAAuB,SAAS,YAAY;GAC5C,aAAa;EACf;CACF;CAGA,MAAM,UAAU,UAAU,aAAa;CACvC,IAAI,YAAY,OACd,MAAM,IAAI,MACR,8EAAiD,mBAAmBA,wBAAW,IAAI,EAAE,wBACvF;CAMF,MAAM,iEAAmC,MAHX,QAAQ;EAAE,GAAG;EAAS,QAAQ;CAAc,CAAC,GAGjB,UAAU,OAAO;CAE3E,MAAM,mBAAmB;CAMzB,MAAM,eAAc,MAJW,QAAQ;EACrC,GAAG;EACH,QAAQ;CACV,CAAC,GACoC,SAAS,gBAAgB;CAE9D,OAAO;EACL;EACA,sCAAuB,SAAS,YAAY;EAC5C;CACF;AACF;;;;AASA,MAAa,wBAAwB,OACnC,UACA,UACA,kBAKG;CACH,MAAM,gBAAgBC,uEAAqB,UAAU,QAAQ;CAQ7D,OAAO;EACL;EACA,GAAG,MARuB,wBAC1B,UACA,eACA,aACF;CAKA;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extractDictionaryKey.cjs","names":["COMPILER_DICTIONARY_KEY_PREFIX"],"sources":["../../../../src/extractContent/utils/extractDictionaryKey.ts"],"sourcesContent":["import { detectExportedComponentName } from '@intlayer/chokidar/cli';\nimport { COMPILER_DICTIONARY_KEY_PREFIX } from '@intlayer/config/defaultValues';\nimport { camelCaseToKebabCase } from '@intlayer/config/utils';\n\n/**\n * Extracts a dictionary key from a file path.\n *\n * Example: \"src/components/MyComponent/index.tsx\" -> \"comp-my-component\"\n */\nexport const extractDictionaryKeyFromPath = (\n filePath: string,\n prefix = COMPILER_DICTIONARY_KEY_PREFIX\n): string => {\n const pathParts = filePath.split(/[\\\\/]/);\n const fileNameWithExt = pathParts.pop() || '';\n const lastDotIndex = fileNameWithExt.lastIndexOf('.');\n let baseName =\n lastDotIndex !== -1\n ? fileNameWithExt.slice(0, lastDotIndex)\n : fileNameWithExt;\n\n if (baseName.toLowerCase() === 'index') {\n baseName = pathParts.pop() || baseName;\n }\n\n return `${prefix}${camelCaseToKebabCase(baseName)}`;\n};\n\nexport const extractDictionaryKey = (\n filePath: string,\n fileText: string,\n prefix = COMPILER_DICTIONARY_KEY_PREFIX\n): string => {\n const componentName = detectExportedComponentName(fileText);\n\n if (componentName) {\n return `${prefix}${camelCaseToKebabCase(componentName)}`;\n }\n\n return extractDictionaryKeyFromPath(filePath, prefix);\n};\n"],"mappings":";;;;;;;;;;;;AASA,MAAa,gCACX,UACA,SAASA,kEACE;CACX,MAAM,YAAY,SAAS,MAAM,
|
|
1
|
+
{"version":3,"file":"extractDictionaryKey.cjs","names":["COMPILER_DICTIONARY_KEY_PREFIX"],"sources":["../../../../src/extractContent/utils/extractDictionaryKey.ts"],"sourcesContent":["import { detectExportedComponentName } from '@intlayer/chokidar/cli';\nimport { COMPILER_DICTIONARY_KEY_PREFIX } from '@intlayer/config/defaultValues';\nimport { camelCaseToKebabCase } from '@intlayer/config/utils';\n\n/**\n * Extracts a dictionary key from a file path.\n *\n * Example: \"src/components/MyComponent/index.tsx\" -> \"comp-my-component\"\n */\nexport const extractDictionaryKeyFromPath = (\n filePath: string,\n prefix = COMPILER_DICTIONARY_KEY_PREFIX\n): string => {\n const pathParts = filePath.split(/[\\\\/]/);\n const fileNameWithExt = pathParts.pop() || '';\n const lastDotIndex = fileNameWithExt.lastIndexOf('.');\n let baseName =\n lastDotIndex !== -1\n ? fileNameWithExt.slice(0, lastDotIndex)\n : fileNameWithExt;\n\n if (baseName.toLowerCase() === 'index') {\n baseName = pathParts.pop() || baseName;\n }\n\n return `${prefix}${camelCaseToKebabCase(baseName)}`;\n};\n\nexport const extractDictionaryKey = (\n filePath: string,\n fileText: string,\n prefix = COMPILER_DICTIONARY_KEY_PREFIX\n): string => {\n const componentName = detectExportedComponentName(fileText);\n\n if (componentName) {\n return `${prefix}${camelCaseToKebabCase(componentName)}`;\n }\n\n return extractDictionaryKeyFromPath(filePath, prefix);\n};\n"],"mappings":";;;;;;;;;;;;AASA,MAAa,gCACX,UACA,SAASA,kEACE;CACX,MAAM,YAAY,SAAS,MAAM,OAAO;CACxC,MAAM,kBAAkB,UAAU,IAAI,KAAK;CAC3C,MAAM,eAAe,gBAAgB,YAAY,GAAG;CACpD,IAAI,WACF,iBAAiB,KACb,gBAAgB,MAAM,GAAG,YAAY,IACrC;CAEN,IAAI,SAAS,YAAY,MAAM,SAC7B,WAAW,UAAU,IAAI,KAAK;CAGhC,OAAO,GAAG,0DAA8B,QAAQ;AAClD;AAEA,MAAa,wBACX,UACA,UACA,SAASA,kEACE;CACX,MAAM,wEAA4C,QAAQ;CAE1D,IAAI,eACF,OAAO,GAAG,0DAA8B,aAAa;CAGvD,OAAO,6BAA6B,UAAU,MAAM;AACtD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateKey.cjs","names":[],"sources":["../../../../src/extractContent/utils/generateKey.ts"],"sourcesContent":["export const generateKey = (\n text: string,\n existingKeys: Set<string>\n): string => {\n const maxWords = 5;\n const maxWordLength = 20;\n const maxKeyLength = 40;\n\n let key = text\n .normalize('NFD') // Normalize to decomposes combined characters (e.g., é -> e + ´)\n .replace(/[\\u0300-\\u036f]/g, '') // Remove the accent characters\n .replace(/[\\s_-]+/g, ' ')\n .replace(/[^\\p{L}\\p{N} ]/gu, '')\n .trim()\n .split(' ')\n .filter(Boolean)\n .slice(0, maxWords)\n .map((word) =>\n word.length > maxWordLength ? word.substring(0, maxWordLength) : word\n )\n .map((word, index) =>\n index === 0\n ? word.toLowerCase()\n : word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()\n )\n .join('');\n\n if (key.length > maxKeyLength) {\n key = key.substring(0, maxKeyLength);\n }\n\n if (!key) key = 'content';\n\n // If the key starts with a number, prepend 'x' to make it a valid JS identifier\n if (/^[0-9]/.test(key)) {\n key = `x${key}`;\n }\n\n if (existingKeys.has(key)) {\n let i = 1;\n while (existingKeys.has(`${key}${i}`)) i++;\n key = `${key}${i}`;\n }\n return key;\n};\n"],"mappings":";;;AAAA,MAAa,eACX,MACA,iBACW;CACX,MAAM,WAAW;CACjB,MAAM,gBAAgB;CACtB,MAAM,eAAe;CAErB,IAAI,MAAM,KACP,UAAU,
|
|
1
|
+
{"version":3,"file":"generateKey.cjs","names":[],"sources":["../../../../src/extractContent/utils/generateKey.ts"],"sourcesContent":["export const generateKey = (\n text: string,\n existingKeys: Set<string>\n): string => {\n const maxWords = 5;\n const maxWordLength = 20;\n const maxKeyLength = 40;\n\n let key = text\n .normalize('NFD') // Normalize to decomposes combined characters (e.g., é -> e + ´)\n .replace(/[\\u0300-\\u036f]/g, '') // Remove the accent characters\n .replace(/[\\s_-]+/g, ' ')\n .replace(/[^\\p{L}\\p{N} ]/gu, '')\n .trim()\n .split(' ')\n .filter(Boolean)\n .slice(0, maxWords)\n .map((word) =>\n word.length > maxWordLength ? word.substring(0, maxWordLength) : word\n )\n .map((word, index) =>\n index === 0\n ? word.toLowerCase()\n : word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()\n )\n .join('');\n\n if (key.length > maxKeyLength) {\n key = key.substring(0, maxKeyLength);\n }\n\n if (!key) key = 'content';\n\n // If the key starts with a number, prepend 'x' to make it a valid JS identifier\n if (/^[0-9]/.test(key)) {\n key = `x${key}`;\n }\n\n if (existingKeys.has(key)) {\n let i = 1;\n while (existingKeys.has(`${key}${i}`)) i++;\n key = `${key}${i}`;\n }\n return key;\n};\n"],"mappings":";;;AAAA,MAAa,eACX,MACA,iBACW;CACX,MAAM,WAAW;CACjB,MAAM,gBAAgB;CACtB,MAAM,eAAe;CAErB,IAAI,MAAM,KACP,UAAU,KAAK,EACf,QAAQ,oBAAoB,EAAE,EAC9B,QAAQ,YAAY,GAAG,EACvB,QAAQ,oBAAoB,EAAE,EAC9B,KAAK,EACL,MAAM,GAAG,EACT,OAAO,OAAO,EACd,MAAM,GAAG,QAAQ,EACjB,KAAK,SACJ,KAAK,SAAS,gBAAgB,KAAK,UAAU,GAAG,aAAa,IAAI,IACnE,EACC,KAAK,MAAM,UACV,UAAU,IACN,KAAK,YAAY,IACjB,KAAK,OAAO,CAAC,EAAE,YAAY,IAAI,KAAK,MAAM,CAAC,EAAE,YAAY,CAC/D,EACC,KAAK,EAAE;CAEV,IAAI,IAAI,SAAS,cACf,MAAM,IAAI,UAAU,GAAG,YAAY;CAGrC,IAAI,CAAC,KAAK,MAAM;CAGhB,IAAI,SAAS,KAAK,GAAG,GACnB,MAAM,IAAI;CAGZ,IAAI,aAAa,IAAI,GAAG,GAAG;EACzB,IAAI,IAAI;EACR,OAAO,aAAa,IAAI,GAAG,MAAM,GAAG,GAAG;EACvC,MAAM,GAAG,MAAM;CACjB;CACA,OAAO;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getComponentName.cjs","names":["t"],"sources":["../../../../src/extractContent/utils/getComponentName.ts"],"sourcesContent":["import type { NodePath } from '@babel/traverse';\nimport * as t from '@babel/types';\n\n/**\n * Resolves the name of the component from various function declaration types.\n */\nexport const getComponentName = (path: NodePath): string | undefined => {\n if (path.isFunctionDeclaration()) {\n return path.node.id?.name;\n }\n\n if (path.isArrowFunctionExpression() || path.isFunctionExpression()) {\n if (\n path.parentPath.isVariableDeclarator() &&\n t.isIdentifier(path.parentPath.node.id)\n ) {\n return path.parentPath.node.id.name;\n }\n }\n\n return undefined;\n};\n"],"mappings":";;;;;;;;;AAMA,MAAa,oBAAoB,SAAuC;CACtE,IAAI,KAAK,
|
|
1
|
+
{"version":3,"file":"getComponentName.cjs","names":["t"],"sources":["../../../../src/extractContent/utils/getComponentName.ts"],"sourcesContent":["import type { NodePath } from '@babel/traverse';\nimport * as t from '@babel/types';\n\n/**\n * Resolves the name of the component from various function declaration types.\n */\nexport const getComponentName = (path: NodePath): string | undefined => {\n if (path.isFunctionDeclaration()) {\n return path.node.id?.name;\n }\n\n if (path.isArrowFunctionExpression() || path.isFunctionExpression()) {\n if (\n path.parentPath.isVariableDeclarator() &&\n t.isIdentifier(path.parentPath.node.id)\n ) {\n return path.parentPath.node.id.name;\n }\n }\n\n return undefined;\n};\n"],"mappings":";;;;;;;;;AAMA,MAAa,oBAAoB,SAAuC;CACtE,IAAI,KAAK,sBAAsB,GAC7B,OAAO,KAAK,KAAK,IAAI;CAGvB,IAAI,KAAK,0BAA0B,KAAK,KAAK,qBAAqB,GAChE;MACE,KAAK,WAAW,qBAAqB,KACrCA,aAAE,aAAa,KAAK,WAAW,KAAK,EAAE,GAEtC,OAAO,KAAK,WAAW,KAAK,GAAG;CACjC;AAIJ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getExistingIntlayerInfo.cjs","names":["t"],"sources":["../../../../src/extractContent/utils/getExistingIntlayerInfo.ts"],"sourcesContent":["import type { NodePath } from '@babel/traverse';\nimport * as t from '@babel/types';\n\nexport type ExistingIntlayerInfo = {\n key: string;\n hook: 'useIntlayer' | 'getIntlayer';\n /** The variable name used to store the call result (e.g. `t` in `const t = useIntlayer(...)`) */\n variableName: string;\n /** True when the call result is destructured: `const { a, b } = getIntlayer(...)` */\n isDestructured: boolean;\n /** Keys already present in the destructuring pattern (empty when not destructured) */\n existingDestructuredKeys: string[];\n /** The ObjectPattern AST node, present only when `isDestructured` is true */\n objectPatternNode?: t.ObjectPattern;\n};\n\n/**\n * Searches for an existing useIntlayer or getIntlayer call in the function body.\n */\nexport const getExistingIntlayerInfo = (\n path: NodePath\n): ExistingIntlayerInfo | undefined => {\n let result: ExistingIntlayerInfo | undefined;\n\n // We only check the direct body of the function, not nested functions\n path.traverse({\n CallExpression(childPath) {\n const callee = childPath.node.callee;\n\n if (\n t.isIdentifier(callee) &&\n (callee.name === 'useIntlayer' || callee.name === 'getIntlayer')\n ) {\n const arg = childPath.node.arguments[0];\n\n if (t.isStringLiteral(arg)) {\n const parentNode = childPath.parent;\n let isDestructured = false;\n let existingDestructuredKeys: string[] = [];\n let objectPatternNode: t.ObjectPattern | undefined;\n let variableName = 'content';\n\n if (t.isVariableDeclarator(parentNode)) {\n if (t.isObjectPattern(parentNode.id)) {\n isDestructured = true;\n objectPatternNode = parentNode.id;\n existingDestructuredKeys = parentNode.id.properties\n .filter(\n (p): p is t.ObjectProperty =>\n t.isObjectProperty(p) && t.isIdentifier(p.key)\n )\n .map((p) => (p.key as t.Identifier).name);\n } else if (t.isIdentifier(parentNode.id)) {\n variableName = parentNode.id.name;\n }\n }\n\n result = {\n key: arg.value,\n hook: callee.name as 'useIntlayer' | 'getIntlayer',\n variableName,\n isDestructured,\n existingDestructuredKeys,\n objectPatternNode,\n };\n childPath.stop();\n }\n }\n },\n Function(childPath) {\n childPath.skip(); // Don't look inside nested functions\n },\n });\n\n return result;\n};\n"],"mappings":";;;;;;;;;AAmBA,MAAa,2BACX,SACqC;CACrC,IAAI;CAGJ,KAAK,SAAS;EACZ,eAAe,WAAW;GACxB,MAAM,SAAS,UAAU,KAAK;GAE9B,IACEA,aAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"getExistingIntlayerInfo.cjs","names":["t"],"sources":["../../../../src/extractContent/utils/getExistingIntlayerInfo.ts"],"sourcesContent":["import type { NodePath } from '@babel/traverse';\nimport * as t from '@babel/types';\n\nexport type ExistingIntlayerInfo = {\n key: string;\n hook: 'useIntlayer' | 'getIntlayer';\n /** The variable name used to store the call result (e.g. `t` in `const t = useIntlayer(...)`) */\n variableName: string;\n /** True when the call result is destructured: `const { a, b } = getIntlayer(...)` */\n isDestructured: boolean;\n /** Keys already present in the destructuring pattern (empty when not destructured) */\n existingDestructuredKeys: string[];\n /** The ObjectPattern AST node, present only when `isDestructured` is true */\n objectPatternNode?: t.ObjectPattern;\n};\n\n/**\n * Searches for an existing useIntlayer or getIntlayer call in the function body.\n */\nexport const getExistingIntlayerInfo = (\n path: NodePath\n): ExistingIntlayerInfo | undefined => {\n let result: ExistingIntlayerInfo | undefined;\n\n // We only check the direct body of the function, not nested functions\n path.traverse({\n CallExpression(childPath) {\n const callee = childPath.node.callee;\n\n if (\n t.isIdentifier(callee) &&\n (callee.name === 'useIntlayer' || callee.name === 'getIntlayer')\n ) {\n const arg = childPath.node.arguments[0];\n\n if (t.isStringLiteral(arg)) {\n const parentNode = childPath.parent;\n let isDestructured = false;\n let existingDestructuredKeys: string[] = [];\n let objectPatternNode: t.ObjectPattern | undefined;\n let variableName = 'content';\n\n if (t.isVariableDeclarator(parentNode)) {\n if (t.isObjectPattern(parentNode.id)) {\n isDestructured = true;\n objectPatternNode = parentNode.id;\n existingDestructuredKeys = parentNode.id.properties\n .filter(\n (p): p is t.ObjectProperty =>\n t.isObjectProperty(p) && t.isIdentifier(p.key)\n )\n .map((p) => (p.key as t.Identifier).name);\n } else if (t.isIdentifier(parentNode.id)) {\n variableName = parentNode.id.name;\n }\n }\n\n result = {\n key: arg.value,\n hook: callee.name as 'useIntlayer' | 'getIntlayer',\n variableName,\n isDestructured,\n existingDestructuredKeys,\n objectPatternNode,\n };\n childPath.stop();\n }\n }\n },\n Function(childPath) {\n childPath.skip(); // Don't look inside nested functions\n },\n });\n\n return result;\n};\n"],"mappings":";;;;;;;;;AAmBA,MAAa,2BACX,SACqC;CACrC,IAAI;CAGJ,KAAK,SAAS;EACZ,eAAe,WAAW;GACxB,MAAM,SAAS,UAAU,KAAK;GAE9B,IACEA,aAAE,aAAa,MAAM,MACpB,OAAO,SAAS,iBAAiB,OAAO,SAAS,gBAClD;IACA,MAAM,MAAM,UAAU,KAAK,UAAU;IAErC,IAAIA,aAAE,gBAAgB,GAAG,GAAG;KAC1B,MAAM,aAAa,UAAU;KAC7B,IAAI,iBAAiB;KACrB,IAAI,2BAAqC,CAAC;KAC1C,IAAI;KACJ,IAAI,eAAe;KAEnB,IAAIA,aAAE,qBAAqB,UAAU,GACnC;UAAIA,aAAE,gBAAgB,WAAW,EAAE,GAAG;OACpC,iBAAiB;OACjB,oBAAoB,WAAW;OAC/B,2BAA2B,WAAW,GAAG,WACtC,QACE,MACCA,aAAE,iBAAiB,CAAC,KAAKA,aAAE,aAAa,EAAE,GAAG,CACjD,EACC,KAAK,MAAO,EAAE,IAAqB,IAAI;MAC5C,OAAO,IAAIA,aAAE,aAAa,WAAW,EAAE,GACrC,eAAe,WAAW,GAAG;KAC/B;KAGF,SAAS;MACP,KAAK,IAAI;MACT,MAAM,OAAO;MACb;MACA;MACA;MACA;KACF;KACA,UAAU,KAAK;IACjB;GACF;EACF;EACA,SAAS,WAAW;GAClB,UAAU,KAAK;EACjB;CACF,CAAC;CAED,OAAO;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getOrGenerateKey.cjs","names":["generateKey"],"sources":["../../../../src/extractContent/utils/getOrGenerateKey.ts"],"sourcesContent":["import { generateKey } from './generateKey';\n\n/**\n * Gets an existing key for a given text or generates a new one.\n */\nexport const getOrGenerateKey = (\n text: string,\n componentKey: string,\n existingKeys: Set<string>,\n extractedContent: Record<string, Record<string, string>>\n): string => {\n if (!extractedContent[componentKey]) {\n extractedContent[componentKey] = {};\n }\n const existingEntry = Object.entries(extractedContent[componentKey]).find(\n ([_, value]) => value === text\n );\n\n if (existingEntry) {\n return existingEntry[0];\n }\n const key = generateKey(text, existingKeys);\n\n existingKeys.add(key);\n extractedContent[componentKey][key] = text;\n return key;\n};\n"],"mappings":";;;;;;;AAKA,MAAa,oBACX,MACA,cACA,cACA,qBACW;CACX,IAAI,CAAC,iBAAiB,eACpB,iBAAiB,gBAAgB,
|
|
1
|
+
{"version":3,"file":"getOrGenerateKey.cjs","names":["generateKey"],"sources":["../../../../src/extractContent/utils/getOrGenerateKey.ts"],"sourcesContent":["import { generateKey } from './generateKey';\n\n/**\n * Gets an existing key for a given text or generates a new one.\n */\nexport const getOrGenerateKey = (\n text: string,\n componentKey: string,\n existingKeys: Set<string>,\n extractedContent: Record<string, Record<string, string>>\n): string => {\n if (!extractedContent[componentKey]) {\n extractedContent[componentKey] = {};\n }\n const existingEntry = Object.entries(extractedContent[componentKey]).find(\n ([_, value]) => value === text\n );\n\n if (existingEntry) {\n return existingEntry[0];\n }\n const key = generateKey(text, existingKeys);\n\n existingKeys.add(key);\n extractedContent[componentKey][key] = text;\n return key;\n};\n"],"mappings":";;;;;;;AAKA,MAAa,oBACX,MACA,cACA,cACA,qBACW;CACX,IAAI,CAAC,iBAAiB,eACpB,iBAAiB,gBAAgB,CAAC;CAEpC,MAAM,gBAAgB,OAAO,QAAQ,iBAAiB,aAAa,EAAE,MAClE,CAAC,GAAG,WAAW,UAAU,IAC5B;CAEA,IAAI,eACF,OAAO,cAAc;CAEvB,MAAM,MAAMA,qDAAY,MAAM,YAAY;CAE1C,aAAa,IAAI,GAAG;CACpB,iBAAiB,cAAc,OAAO;CACtC,OAAO;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveDictionaryKey.cjs","names":[],"sources":["../../../../src/extractContent/utils/resolveDictionaryKey.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport { assertPathWithin } from '@intlayer/config/utils';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\n\n/**\n * Resolves a unique dictionary key, checking for existing dictionaries and files.\n * Note: this chokidar-specific variant fetches unmergedDictionaries internally\n * from the configuration (unlike the `@intlayer/babel` version which takes it as a parameter).\n */\nexport const resolveDictionaryKey = (\n initialKey: string,\n filePath: string,\n configuration: IntlayerConfig,\n unmergedDictionaries?: Record<string, unknown>,\n usedKeys: Set<string> = new Set()\n): string => {\n const dicts =\n unmergedDictionaries ?? getUnmergedDictionaries(configuration) ?? {};\n\n const { fileExtensions } = configuration.content;\n\n const dirName = dirname(filePath);\n const firstExtension = fileExtensions[0];\n const extension = firstExtension.startsWith('.')\n ? firstExtension\n : `.${firstExtension}`;\n\n let index = 0;\n\n while (index < 100) {\n const keyToTest = index === 0 ? initialKey : `${initialKey}${index}`;\n const dictionaries = dicts[keyToTest] as Dictionary[] | undefined;\n const contentFilePath = join(dirName, `${keyToTest}${extension}`);\n\n assertPathWithin(resolve(contentFilePath), dirName);\n\n const isKeyUsed = usedKeys.has(keyToTest);\n const hasDictionaries = dictionaries && dictionaries.length > 0;\n const fileExists = existsSync(contentFilePath);\n\n if (!isKeyUsed && !hasDictionaries && !fileExists) {\n return keyToTest;\n }\n index++;\n }\n\n return initialKey;\n};\n"],"mappings":";;;;;;;;;;;;;AAYA,MAAa,wBACX,YACA,UACA,eACA,sBACA,2BAAwB,IAAI,
|
|
1
|
+
{"version":3,"file":"resolveDictionaryKey.cjs","names":[],"sources":["../../../../src/extractContent/utils/resolveDictionaryKey.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport { assertPathWithin } from '@intlayer/config/utils';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\n\n/**\n * Resolves a unique dictionary key, checking for existing dictionaries and files.\n * Note: this chokidar-specific variant fetches unmergedDictionaries internally\n * from the configuration (unlike the `@intlayer/babel` version which takes it as a parameter).\n */\nexport const resolveDictionaryKey = (\n initialKey: string,\n filePath: string,\n configuration: IntlayerConfig,\n unmergedDictionaries?: Record<string, unknown>,\n usedKeys: Set<string> = new Set()\n): string => {\n const dicts =\n unmergedDictionaries ?? getUnmergedDictionaries(configuration) ?? {};\n\n const { fileExtensions } = configuration.content;\n\n const dirName = dirname(filePath);\n const firstExtension = fileExtensions[0];\n const extension = firstExtension.startsWith('.')\n ? firstExtension\n : `.${firstExtension}`;\n\n let index = 0;\n\n while (index < 100) {\n const keyToTest = index === 0 ? initialKey : `${initialKey}${index}`;\n const dictionaries = dicts[keyToTest] as Dictionary[] | undefined;\n const contentFilePath = join(dirName, `${keyToTest}${extension}`);\n\n assertPathWithin(resolve(contentFilePath), dirName);\n\n const isKeyUsed = usedKeys.has(keyToTest);\n const hasDictionaries = dictionaries && dictionaries.length > 0;\n const fileExists = existsSync(contentFilePath);\n\n if (!isKeyUsed && !hasDictionaries && !fileExists) {\n return keyToTest;\n }\n index++;\n }\n\n return initialKey;\n};\n"],"mappings":";;;;;;;;;;;;;AAYA,MAAa,wBACX,YACA,UACA,eACA,sBACA,2BAAwB,IAAI,IAAI,MACrB;CACX,MAAM,QACJ,2FAAgD,aAAa,KAAK,CAAC;CAErE,MAAM,EAAE,mBAAmB,cAAc;CAEzC,MAAM,iCAAkB,QAAQ;CAChC,MAAM,iBAAiB,eAAe;CACtC,MAAM,YAAY,eAAe,WAAW,GAAG,IAC3C,iBACA,IAAI;CAER,IAAI,QAAQ;CAEZ,OAAO,QAAQ,KAAK;EAClB,MAAM,YAAY,UAAU,IAAI,aAAa,GAAG,aAAa;EAC7D,MAAM,eAAe,MAAM;EAC3B,MAAM,sCAAuB,SAAS,GAAG,YAAY,WAAW;EAEhE,oEAAyB,eAAe,GAAG,OAAO;EAElD,MAAM,YAAY,SAAS,IAAI,SAAS;EACxC,MAAM,kBAAkB,gBAAgB,aAAa,SAAS;EAC9D,MAAM,qCAAwB,eAAe;EAE7C,IAAI,CAAC,aAAa,CAAC,mBAAmB,CAAC,YACrC,OAAO;EAET;CACF;CAEA,OAAO;AACT"}
|
|
@@ -18,6 +18,11 @@ const shouldExtract = (text) => {
|
|
|
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;
|
|
21
|
+
const cssClassTokenRegex = /^!?([a-z][a-z0-9]*:)*[a-z][a-z0-9]*(-[a-z0-9[\].,%#/]+)*(\/[a-z0-9]+)?$/;
|
|
22
|
+
if (wordCount > 1) {
|
|
23
|
+
const tokens = trimmed.split(/\s+/);
|
|
24
|
+
if (tokens.every((token) => cssClassTokenRegex.test(token)) && tokens.some((token) => token.includes("-"))) return false;
|
|
25
|
+
}
|
|
21
26
|
const isCapitalized = /^['"([]*\p{Lu}/u.test(trimmed);
|
|
22
27
|
if (wordCount === 1) {
|
|
23
28
|
if (/[a-z]\p{Lu}/u.test(trimmed)) return false;
|