{"record":{"id":"ec82335e39c9b148","repo":"remotion-dev/remotion","slug":"could-not-locate-the-jsx-element-to-move-source-edits","errorCode":null,"errorMessage":"Could not locate the JSX element to move","messagePattern":"Could not locate the JSX element to move","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/studio-codemods/src/source-edits.ts","lineNumber":68,"sourceCode":"\t\t\t\t\t.replaceAll(\"'\", '&apos;')\n\t\t\t\t\t.replaceAll('<', '&lt;') +\n\t\t\t\tquote\n\t\t\t: recast.prettyPrint(recast.types.builders.stringLiteral(newValue), {\n\t\t\t\t\tquote: quote === \"'\" ? 'single' : 'double',\n\t\t\t\t}).code;\n\n\treturn {start, end, replacement};\n};\n\nexport const getJsxElementSourceForInsertion = ({\n\telement,\n\tinput,\n}: {\n\telement: JSXElement;\n\tinput: string;\n}): string => {\n\tif (!element.loc) {\n\t\tthrow new Error('Could not locate the JSX element to move');\n\t}\n\n\tconst start = recastLocToOffset(input, element.loc.start);\n\tconst end = recastLocToOffset(input, element.loc.end);\n\tconst originalIndent = getLineIndent({input, offset: start});\n\n\treturn input\n\t\t.slice(start, end)\n\t\t.split(/\\r?\\n/)\n\t\t.map((line, index) => {\n\t\t\tif (index === 0) {\n\t\t\t\treturn line;\n\t\t\t}\n\n\t\t\treturn line.startsWith(originalIndent)\n\t\t\t\t? line.slice(originalIndent.length)\n\t\t\t\t: line.trimStart();\n\t\t})","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/remotion-dev/remotion/blob/46a3a6bf13ca367376ff18bbb06dae2e87e0d068/packages/studio-codemods/src/source-edits.ts#L50-L86","documentation":"getJsxElementSourceForInsertion returns the original source string of a JSX element so it can be re-inserted elsewhere. It throws when the JSXElement AST node has no source location (element.loc is null), which makes it impossible to recover the element's text or its indentation.","triggerScenarios":"Calling this helper with a JSXElement that was created or cloned programmatically during the codemod (no loc), or with an element from a second parse that was not passed through recast's location-recording parser.","commonSituations":"Chained codemods where a transform builds a new JSXElement and another helper is expected to move it; cloning a node with {...node} without loc; parsing with a parser option that strips loc data.","solutions":["Only pass JSXElements obtained from the recast-parsed original source; do not pass nodes you constructed yourself.","If you cloned a node, preserve loc (or re-derive it) before calling, e.g. by keeping the original node reference.","Re-parse the current source with recast so all nodes have loc, then locate the element again.","Fall back to serializing the node with recast.print if you control the caller and loc cannot be restored."],"exampleFix":"// before: passing a synthesized element\nconst text = getJsxElementSourceForInsertion({element: newElement, input});\n// after: use the element found in the parsed tree\nconst parsed = parse(input).program.body[0]...;\nconst text = getJsxElementSourceForInsertion({element: parsedElement, input});","handlingStrategy":"type-guard","validationCode":"if (!element.loc) {\n  throw new Error('Element must come from a recast-parsed source tree');\n}","typeGuard":"const isLocatable = <T extends {loc: unknown}>(node: T): node is T & {loc: NonNullable<T['loc']>} => node.loc != null;","tryCatchPattern":"try {\n  const src = getJsxElementSourceForInsertion({element, input});\n} catch (err) {\n  // fall back to recast.print(element).code\n}","preventionTips":["Always source JSXElement nodes from the recast parse of the file","Do not clone or spread AST nodes; keep original references","Re-parse after any in-memory mutation before computing source edits","Assert node.loc exists at codemod entry points"],"tags":["codemod","jsx","ast","missing-loc"],"backgroundTag":"resource-not-found","analyzedSha":"46a3a6bf13ca367376ff18bbb06dae2e87e0d068","analyzedAt":"2026-09-18T00:12:20.256Z","contentChangedAt":"2026-09-18T00:12:20.256Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}