@drangis-tech/file-writer
Version:
TSX file writer utility using ts-morph
1 lines • 5.48 kB
Source Map (JSON)
{"version":3,"sources":["../src/writer.ts"],"sourcesContent":["import { Project, SyntaxKind, JsxAttribute } from \"ts-morph\";\nimport type { Change, DesignMap } from \"./types\";\n\nexport class FileWriter {\n private project: Project;\n\n constructor() {\n this.project = new Project({\n skipFileDependencyResolution: true,\n });\n }\n\n /**\n * Apply a change to a TSX file based on the design map.\n */\n async applyChange(\n change: Change,\n designMap: DesignMap,\n projectRoot: string\n ): Promise<void> {\n const entry = designMap[change.id];\n if (!entry) {\n throw new Error(`No mapping found for id: ${change.id}`);\n }\n\n const filePath = `${projectRoot}/${entry.file}`;\n const sourceFile = this.project.addSourceFileAtPath(filePath);\n\n // Find the JSX element with matching data-edit-id\n const jsxElements = sourceFile.getDescendantsOfKind(\n SyntaxKind.JsxSelfClosingElement\n );\n const jsxOpeningElements = sourceFile.getDescendantsOfKind(\n SyntaxKind.JsxOpeningElement\n );\n\n let targetElement: any = null;\n\n // Check self-closing elements\n for (const el of jsxElements) {\n const attr = el.getAttribute(\"data-edit-id\");\n if (attr && attr.getText().includes(`\"${change.id}\"`)) {\n targetElement = el;\n break;\n }\n }\n\n // Check opening elements\n if (!targetElement) {\n for (const el of jsxOpeningElements) {\n const attr = el.getAttribute(\"data-edit-id\");\n if (attr && attr.getText().includes(`\"${change.id}\"`)) {\n targetElement = el;\n break;\n }\n }\n }\n\n if (!targetElement) {\n throw new Error(\n `Could not find JSX element with data-edit-id=\"${change.id}\" in ${entry.file}`\n );\n }\n\n if (change.kind === \"text\") {\n // For text changes, we need the parent JsxElement (not self-closing)\n const parent = targetElement.getParent();\n if (parent && parent.getKind() === SyntaxKind.JsxElement) {\n const jsxElement = parent;\n const children = jsxElement.getJsxChildren();\n \n // Find first JsxText child\n const textNode = children.find(\n (child: any) => child.getKind() === SyntaxKind.JsxText\n );\n\n if (textNode) {\n // Replace existing text\n textNode.replaceWithText(change.value);\n } else {\n // Insert new text after opening element\n const openingElement = jsxElement.getOpeningElement();\n openingElement.insertText(openingElement.getEnd(), change.value);\n }\n } else {\n throw new Error(\n `Element with data-edit-id=\"${change.id}\" is self-closing and cannot contain text. Please use a non-self-closing element.`\n );\n }\n } else if (change.kind === \"classes\") {\n // For className changes\n let classNameAttr = targetElement.getAttribute(\"className\") as\n | JsxAttribute\n | undefined;\n\n if (classNameAttr) {\n // Update existing className\n const initializer = classNameAttr.getInitializer();\n if (initializer) {\n initializer.replaceWithText(`\"${change.value}\"`);\n } else {\n classNameAttr.setInitializer(`\"${change.value}\"`);\n }\n } else {\n // Add new className attribute\n targetElement.addAttribute({\n name: \"className\",\n initializer: `\"${change.value}\"`,\n });\n }\n }\n\n await sourceFile.save();\n\n // Remove from project to avoid memory leaks\n this.project.removeSourceFile(sourceFile);\n }\n}\n\n"],"mappings":";AAAA,SAAS,SAAS,kBAAgC;AAG3C,IAAM,aAAN,MAAiB;AAAA,EAGtB,cAAc;AACZ,SAAK,UAAU,IAAI,QAAQ;AAAA,MACzB,8BAA8B;AAAA,IAChC,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YACJ,QACA,WACA,aACe;AACf,UAAM,QAAQ,UAAU,OAAO,EAAE;AACjC,QAAI,CAAC,OAAO;AACV,YAAM,IAAI,MAAM,4BAA4B,OAAO,EAAE,EAAE;AAAA,IACzD;AAEA,UAAM,WAAW,GAAG,WAAW,IAAI,MAAM,IAAI;AAC7C,UAAM,aAAa,KAAK,QAAQ,oBAAoB,QAAQ;AAG5D,UAAM,cAAc,WAAW;AAAA,MAC7B,WAAW;AAAA,IACb;AACA,UAAM,qBAAqB,WAAW;AAAA,MACpC,WAAW;AAAA,IACb;AAEA,QAAI,gBAAqB;AAGzB,eAAW,MAAM,aAAa;AAC5B,YAAM,OAAO,GAAG,aAAa,cAAc;AAC3C,UAAI,QAAQ,KAAK,QAAQ,EAAE,SAAS,IAAI,OAAO,EAAE,GAAG,GAAG;AACrD,wBAAgB;AAChB;AAAA,MACF;AAAA,IACF;AAGA,QAAI,CAAC,eAAe;AAClB,iBAAW,MAAM,oBAAoB;AACnC,cAAM,OAAO,GAAG,aAAa,cAAc;AAC3C,YAAI,QAAQ,KAAK,QAAQ,EAAE,SAAS,IAAI,OAAO,EAAE,GAAG,GAAG;AACrD,0BAAgB;AAChB;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,CAAC,eAAe;AAClB,YAAM,IAAI;AAAA,QACR,iDAAiD,OAAO,EAAE,QAAQ,MAAM,IAAI;AAAA,MAC9E;AAAA,IACF;AAEA,QAAI,OAAO,SAAS,QAAQ;AAE1B,YAAM,SAAS,cAAc,UAAU;AACvC,UAAI,UAAU,OAAO,QAAQ,MAAM,WAAW,YAAY;AACxD,cAAM,aAAa;AACnB,cAAM,WAAW,WAAW,eAAe;AAG3C,cAAM,WAAW,SAAS;AAAA,UACxB,CAAC,UAAe,MAAM,QAAQ,MAAM,WAAW;AAAA,QACjD;AAEA,YAAI,UAAU;AAEZ,mBAAS,gBAAgB,OAAO,KAAK;AAAA,QACvC,OAAO;AAEL,gBAAM,iBAAiB,WAAW,kBAAkB;AACpD,yBAAe,WAAW,eAAe,OAAO,GAAG,OAAO,KAAK;AAAA,QACjE;AAAA,MACF,OAAO;AACL,cAAM,IAAI;AAAA,UACR,8BAA8B,OAAO,EAAE;AAAA,QACzC;AAAA,MACF;AAAA,IACF,WAAW,OAAO,SAAS,WAAW;AAEpC,UAAI,gBAAgB,cAAc,aAAa,WAAW;AAI1D,UAAI,eAAe;AAEjB,cAAM,cAAc,cAAc,eAAe;AACjD,YAAI,aAAa;AACf,sBAAY,gBAAgB,IAAI,OAAO,KAAK,GAAG;AAAA,QACjD,OAAO;AACL,wBAAc,eAAe,IAAI,OAAO,KAAK,GAAG;AAAA,QAClD;AAAA,MACF,OAAO;AAEL,sBAAc,aAAa;AAAA,UACzB,MAAM;AAAA,UACN,aAAa,IAAI,OAAO,KAAK;AAAA,QAC/B,CAAC;AAAA,MACH;AAAA,IACF;AAEA,UAAM,WAAW,KAAK;AAGtB,SAAK,QAAQ,iBAAiB,UAAU;AAAA,EAC1C;AACF;","names":[]}