{"version":3,"file":"transformJSONFile.mjs","names":[],"sources":["../../../src/writeContentDeclaration/transformJSONFile.ts"],"sourcesContent":["import type { Dictionary } from '@intlayer/types/dictionary';\nimport * as recast from 'recast';\n\nconst b = recast.types.builders;\nconst n = recast.types.namedTypes;\n\n/**\n * Checks if a value is a plain object (and not null/array)\n */\nconst isPlainObject = (value: unknown): value is Record<string, unknown> => {\n  return typeof value === 'object' && value !== null && !Array.isArray(value);\n};\n\n/**\n * Checks if a recast AST node value matches the incoming primitive value.\n */\nconst isPrimitiveEqual = (astNode: any, val: unknown): boolean => {\n  if (val === null) {\n    return n.Literal.check(astNode) && astNode.value === null;\n  }\n  if (\n    typeof val === 'string' ||\n    typeof val === 'number' ||\n    typeof val === 'boolean'\n  ) {\n    return (\n      (n.Literal.check(astNode) ||\n        n.StringLiteral.check(astNode) ||\n        n.NumericLiteral.check(astNode) ||\n        n.BooleanLiteral.check(astNode)) &&\n      astNode.value === val\n    );\n  }\n  return false;\n};\n\n/**\n * Robustly finds a property in a recast ObjectExpression.\n * Handles quoted (\"key\") or unquoted (key) properties.\n */\nconst getMatchingProperty = (node: any, key: string) => {\n  return node.properties.find((prop: any) => {\n    if (n.Property.check(prop) || n.ObjectProperty.check(prop)) {\n      if (n.Identifier.check(prop.key) && prop.key.name === key) return true;\n      if (n.StringLiteral.check(prop.key) && prop.key.value === key)\n        return true;\n      if (n.Literal.check(prop.key) && prop.key.value === key) return true;\n    }\n    return false;\n  });\n};\n\n/**\n * Recursively builds a clean recast AST node from a plain JS value.\n * Because these nodes lack `loc` tracking, recast is forced to pretty-print them.\n */\nconst buildASTNode = (val: unknown): any => {\n  if (val === null) return b.literal(null);\n\n  if (\n    typeof val === 'string' ||\n    typeof val === 'number' ||\n    typeof val === 'boolean'\n  ) {\n    return b.literal(val);\n  }\n\n  if (Array.isArray(val)) {\n    return b.arrayExpression(val.map((item) => buildASTNode(item)));\n  }\n\n  if (isPlainObject(val)) {\n    return b.objectExpression(\n      Object.entries(val)\n        .filter(([, v]) => v !== undefined)\n        .map(([k, v]) =>\n          b.property('init', b.stringLiteral(k), buildASTNode(v))\n        )\n    );\n  }\n\n  return b.literal(null); // Fallback\n};\n\n/**\n * Recursively updates the AST object literal with new data.\n */\nconst updateObjectLiteral = (node: any, data: Record<string, any>) => {\n  for (const [key, val] of Object.entries(data)) {\n    if (val === undefined) {\n      continue;\n    }\n\n    const existingProp = getMatchingProperty(node, key);\n\n    if (existingProp?.comments) {\n      existingProp.comments.forEach((c: any) => {\n        if ((c.type === 'Line' || c.type === 'CommentLine') && c.trailing) {\n          // Convert to block comment and tag it to force Recast to print it inline\n          c.type = c.type === 'Line' ? 'Block' : 'CommentBlock';\n          c.value = `__INLINE_LINE__${c.value}`;\n          c.leading = false;\n          c.trailing = true;\n        }\n      });\n    }\n\n    if (isPlainObject(val)) {\n      if (existingProp && n.ObjectExpression.check(existingProp.value)) {\n        updateObjectLiteral(existingProp.value, val);\n      } else {\n        if (existingProp) {\n          // Prevent AST invalidation if the primitive value is identical\n          const isIdentical =\n            n.Literal.check(existingProp.value) &&\n            existingProp.value.value === val;\n\n          if (!isIdentical) {\n            existingProp.value = buildASTNode(val);\n          }\n        } else {\n          node.properties.push(\n            b.property('init', b.stringLiteral(key), buildASTNode(val))\n          );\n        }\n      }\n    } else {\n      // Handle primitives and arrays\n      if (existingProp) {\n        // Skip assignment if the primitive value is identical\n        if (!isPrimitiveEqual(existingProp.value, val)) {\n          existingProp.value = buildASTNode(val);\n        }\n      } else {\n        node.properties.push(\n          b.property('init', b.stringLiteral(key), buildASTNode(val))\n        );\n      }\n    }\n  }\n};\n\nexport const transformJSONFile = (\n  fileContent: string,\n  dictionary: Dictionary,\n  noMetadata?: boolean\n): string => {\n  // Wrap content to create valid syntax for the parser\n  const wrappedContent = `const _config = ${fileContent.trim() || '{}'};`;\n\n  let ast: ReturnType<typeof recast.parse>;\n  try {\n    ast = recast.parse(wrappedContent);\n  } catch {\n    // Fallback if parsing failed\n    return JSON.stringify(\n      noMetadata ? dictionary.content : dictionary,\n      null,\n      2\n    );\n  }\n\n  // Navigate the AST to locate the object literal initialized to `_config`\n  const declaration = ast.program.body[0];\n  let objectLiteral: any;\n\n  if (\n    n.VariableDeclaration.check(declaration) &&\n    declaration.declarations.length > 0 &&\n    n.VariableDeclarator.check(declaration.declarations[0]) &&\n    n.ObjectExpression.check(declaration.declarations[0].init)\n  ) {\n    objectLiteral = declaration.declarations[0].init;\n  }\n\n  if (noMetadata) {\n    const metadataProperties = [\n      'id',\n      'locale',\n      'filled',\n      'fill',\n      'title',\n      'description',\n      'tags',\n      'version',\n      'priority',\n      'contentAutoTransformation',\n      '$schema',\n    ];\n\n    // Mutate the array backwards instead of using .filter() to prevent layout invalidation\n    for (let i = objectLiteral.properties.length - 1; i >= 0; i--) {\n      const prop = objectLiteral.properties[i];\n      if (n.Property.check(prop) || n.ObjectProperty.check(prop)) {\n        let propName = '';\n        if (n.Identifier.check(prop.key)) propName = prop.key.name;\n        else if (n.StringLiteral.check(prop.key)) propName = prop.key.value;\n        else if (n.Literal.check(prop.key)) propName = String(prop.key.value);\n\n        if (['key', 'content', ...metadataProperties].includes(propName)) {\n          objectLiteral.properties.splice(i, 1);\n        }\n      }\n    }\n  }\n\n  // Update the AST in place\n  updateObjectLiteral(\n    objectLiteral,\n    noMetadata ? (dictionary.content as Record<string, any>) : dictionary\n  );\n\n  // Print the full AST to utilize the global token stream for inline comments\n  const printedCode = recast.print(ast, {\n    tabWidth: 2,\n    quote: 'double',\n    trailingComma: false,\n  }).code;\n\n  // Extract the target object literal cleanly\n  const startIndex = printedCode.indexOf('{');\n  const endIndex = printedCode.lastIndexOf('}');\n  let finalOutput = printedCode.substring(startIndex, endIndex + 1);\n\n  // Revert the tagged block comment back to an inline line comment and fix comma placement\n  finalOutput = finalOutput.replace(\n    /\\s*\\/\\*__INLINE_LINE__(.*?)\\*\\/(\\s*,?)/g,\n    '$2 //$1'\n  );\n\n  // Collapse empty lines injected by Recast around commented properties\n  finalOutput = finalOutput.replace(/\\n[ \\t]*\\n/g, '\\n');\n\n  return finalOutput;\n};\n"],"mappings":"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"}