Transformieren von JavaScript-Codebasen im großen Maßstab: Nutzung von ChatGPT und CodeMods
Wir werden die Leistungsfähigkeit von Codemods und ChatGPT beim Refactoring von React-Anwendungen in großem Maßstab untersuchen. Dieser Beitrag wurde durch meine jüngste Erfahrung veranlasst, bei der ich Hunderte von Komponenten ändern musste, die von mehreren Teams in einer großen monolithischen Codebasis verwendet wurden.
Ich werde skizzieren, wie Sie Codemods und ChatGPT nutzen können, um eine erfundene Komponente in großem Maßstab von Grund auf zu migrieren. Die Techniken, die wir besprechen, sind verallgemeinerbar und können leicht an Ihre eigenen Anwendungsfälle angepasst werden. Bevor wir eintauchen, lassen Sie uns einige Definitionen klären.
- Codemods sind automatisierte Transformationen, die den Quellcode modifizieren und es einfacher machen, Code über Codebasen hinweg zu standardisieren und umzugestalten. Mit ihren „intelligenteren“ Find-and-Replace-Funktionen sind Codemods ein leistungsstarkes Tool zur Steigerung der Produktivität in der Softwareentwicklung.
- ChatGPT ist ein Tool zur Verarbeitung natürlicher Sprache, das KI-Technologie nutzt, um menschenähnliche Konversationen zu ermöglichen. In unserem Fall werden wir ChatGPT bitten, Codemods für uns zu schreiben.
Monorepos bieten den Vorteil, dass Module, die in der gesamten Codebasis verwendet werden, mit einem einzigen Commit umgestaltet werden können. Dies ist besonders nützlich in Frontend-React-Kontexten, in denen gemeinsam genutzte Komponenten für alle Consumer-Projekte gleichzeitig geändert werden können.
Der Nachteil solcher Refactorings ist jedoch, dass sie nicht immer so einfach sind wie ein „Suchen und Ersetzen“-Vorgang. Mit zunehmender Codebasis wird es immer schwieriger, umfassende Änderungen schnell und sicher vorzunehmen, unabhängig davon, wie gut man mit einem Texteditor umgehen kann. Das Ändern von Zehntausenden von Codezeilen kann eine zeitaufwändige und entmutigende Aufgabe sein.
Betrachten wir zum Beispiel den Fall, in dem ein Team eine neue Schaltfläche mit einer etwas anderen API als die alte Schaltfläche verwenden möchte. Wie kann man den alten Knopf durch den neuen ersetzen, wenn der erstere tausendfach in verschiedenen Anwendungen verwendet wird? Dies ist kein einfacher Eins-zu-Eins-Ersatz, da sich die API der neuen Schaltfläche von der der alten Schaltfläche unterscheidet, wie im folgenden Szenario gezeigt:
Dabei stehen Ihnen verschiedene Vorgehensweisen zur Verfügung:
- Lassen Sie beide Schaltflächen in der Codebasis nebeneinander bestehen und entfernen Sie die ältere schrittweise
- Erstellen Sie die neue Komponente und schreiben Sie die alte Komponente um, damit sie ein Proxy für die neue ist
- Verwenden Sie Codemods, um die Verwendung der alten Komponente automatisch umzugestalten, um die neue an Ort und Stelle zu verwenden.
Ihr erster CodeMod
Facebook hat vor einigen Jahren JSCodeShift veröffentlicht, ein Toolkit zum Ausführen von Codemods über JavaScript-Codebasen und das Tool, das wir verwenden werden. JSCodeShift nimmt eine Datei, wendet eine Transformation auf den Code an und gibt dieselbe Datei zurück:
Diese Transformationen funktionieren, indem sie Code in einen abstrakten Syntaxbaum (AST) parsen und ihn in einen anderen AST umwandeln.
Ein Beispiel dafür finden Sie online unterhttps://astexplorer.net/Hier können Sie links etwas JavaScript-Code eingeben und rechts sehen, wie dieser in einen (leicht einschüchternden) AST-Baum zerlegt wird.
Ein Beispieltransformator ist unten kommentiert. Dies ist eine JavaScript-Funktion, die die jQuery-ähnliche API von JSCodeShift verwendet, um zu navigieren (unter Verwendung von Funktionen wie find
) und den AST zu transformieren (unter Verwendung von Funktionen wie replaceWith
). Machen Sie sich vorerst keine Gedanken über das Auswendiglernen dieser API.
Wenn Sie die folgende Datei haben
import React from "react";
import OldButton from 'old-button'
export const Example = () => {
return <OldButton color="primary" variant="text">Old</OldButton>;
};
jscodeshift -t rename-button.js example.jsx
Ein komplexerer Codemod mit ChatGPT
In unserem vorherigen Beispiel haben wir einen einfachen Codemod geschrieben, der leicht mit der Suchen-und-Ersetzen-Funktion Ihrer IDE ausgeführt werden könnte. Komplexere Refactoring-Aufgaben erfordern jedoch ein tieferes Verständnis der Transformationsfunktionen und der API von CodeShift.
Lassen Sie uns also ein kniffligeres Refactoring durchführen:
Schreiben Sie mir einen jscodeshift-Codemod, der die öffnenden und schließenden Elemente einer Komponente von OldButton in NewButton umbenennt. Importieren Sie NewButton aus 'new-button' und entfernen Sie den alten Import. Benennen Sie den Eigenschaftstyp in Variante um.
Beachten Sie, wie ich dies als Aufforderung formuliert habe. Ich verstehe, wie JSCodeShift funktioniert, ich mag es nur nicht, Fehler bei der Node-Traversierung zu beheben oder alte Snippets auszugraben, die ich zuvor erstellt habe. Genau diese Arbeit können intelligente Assistenten (IA) wie ChatGPT für uns erledigen:
Das Ausführen dieses Skripts über meine Datei bietet den folgenden Unterschied:
Beachten Sie, wie Sie dies auf eine gesamte Codebasis (nicht nur eine einzelne Datei) anwenden können und die Transformation auf alle Dateien angewendet wird.
Sehen Sie sich die vollständige generierte Ausgabe unten an und versuchen Sie zu verstehen, wie jede Transformation funktioniert, während Sie die AST-Struktur erkunden. Mit ein wenig Übung, Dokumentationserforschung und ChatGPT-Unterstützung werden Sie in der Lage sein, die meisten Transformationen zu erstellen und auszuführen. Dies befreit Sie von der Plackerei und lässt Sie sich auf die schönen und kreativen Aspekte des Software Engineering konzentrieren.
module.exports = function(file, api) {
const j = api.jscodeshift;
const rootSource = j(file.source);
// Find all import declarations for OldButton and replace with NewButton
const importDeclaration = rootSource
.find(j.ImportDeclaration, {
source: {
value: 'old-button'
}
})
// Replace these import declarations with a new import declaration
.replaceWith(
j.importDeclaration(
[j.importSpecifier(j.identifier('NewButton'))],
j.stringLiteral('new-button')
)
)
.size() > 0;
// Find all JSXElements with the name "OldButton" and rename them to "NewButton"
const elementRename = rootSource
.find(j.JSXOpeningElement, {
name: {
name: 'OldButton'
}
})
.replaceWith(({ node }) => {
const { attributes } = node;
const variantAttribute = attributes.find(
attribute => attribute.name.name === 'type'
);
// If the node has the desired attribute, replace it
if (variantAttribute) {
variantAttribute.name.name = 'variant';
}
return j.jsxOpeningElement(j.jsxIdentifier('NewButton'), attributes);
})
.size() > 0;
// Find all JSXClosingElements with the name "OldButton" and rename them to "NewButton"
const closingElementRename = rootSource
.find(j.JSXClosingElement, {
name: {
name: 'OldButton'
}
})
.replaceWith(({ node }) => j.jsxClosingElement(j.jsxIdentifier('NewButton')))
.size() > 0;
return rootSource.toSource()
};
Zusammenfassend lässt sich sagen, dass die Kombination von ChatGPT und JSCodeShift eine effektive Lösung für das Refactoring von Legacy-Code in großem Maßstab bietet, sodass Teams Zeit sparen und sich auf Engineering und Innovation konzentrieren können. Es ist jedoch wichtig, Vorsicht walten zu lassen und sich nicht blind auf ChatGPT zu verlassen, um codemod
Anweisungen zu generieren. Nehmen Sie sich stattdessen die Zeit, um zu verstehen, wie JSCodeShift funktioniert, und verwenden Sie ChatGPT als leistungsstarken Assistenten, um Ihren Workflow zu erweitern.
Darüber hinaus wird empfohlen, Best Practices wie das Schreiben von Komponententests für alle Transformatoren und das Implementieren anderer Teststrategien zu befolgen, um sicherzustellen, dass Änderungen keine unerwarteten Fehler in Ihre Codebasis einführen. Indem Sie diese Maßnahmen ergreifen, können Sie das volle Potenzial von ChatGPT und JSCodeShift nutzen, um Ihren Refactoring-Prozess zu rationalisieren und die Qualität Ihres Codes zu verbessern.