{"version":3,"file":"transformJSONFile.cjs","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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"}