JSCodeshift: Automatisierte Vue 3-Migration 

Foto des Autors
Michael Didion

JSCodeshift ist ein nützliches Werkzeug für Entwickler:innen, um zeitaufwendige und fehleranfällige Aufgaben im Zusammenhang mit Codeänderungen zu automatisieren und zu standardisieren. Basierend auf diesem Werkzeug haben wir einen Converter für die Umstellung von VueJS 2 auf VueJS 3 Composition-API entwickelt. Die Verwendung dieses maßgeschneiderten Converters (Link am Ende des Beitrags) hat zu signifikanten Arbeitserleichterungen und Zeitersparnissen geführt.

Während seiner Mitarbeit am DB Navigator hat unser Kollegen Michael Didion wichtige Erfahrungen im Bereich der Migration mit JSCodeshift gesammelt. Um ebendiese wird es im Beitrag gehen.
Quellenangabe: Oleksandr – stock.adobe.com

Als IT-Berater habe ich zehn Monate an der Erneuerung der Vertriebsplattform der Deutsche Bahn (Projektname: Vendo) mitgewirkt. Während meiner Mitarbeit am Kunden-Frontend „bahn.de“ haben Melvin McMonagle, Software-Entwickler bei der Deutschen Bahn, und ich wichtige Erfahrungen im Bereich der Codemigration mit JSCodeshift gesammelt. Um ebendiese wird es im Folgenden gehen: Wir werden euch einen Überblick über die automatisierte Migration von VueJS 2 auf VueJS 3 mit JSCodeShift geben.   

Profilbild von Melvin McMonagle

Co-Autor

Melvin McMonagle

Software Craftsman bei Deutsche Bahn AG Fernverkehr

Warum eine Migration?

Mit dem nahenden Ende des Lebenszyklus für VueJS 2 LTS am 31. Dezember 2023, rücken die Herausforderungen der Migration zu VueJS 3 immer mehr in den Fokus von Entwicklungsteams. Diese Umstellung ist nicht nur wegen des auslaufenden Supports für VueJS 2 wichtig, sondern auch, weil neue Sicherheitsupdates und Plug-in-Unterstützung für diese Version eingestellt werden. In diesem Kontext möchten wir unseren Ansatz zur automatisierten Migration von VueJS 2 auf VueJS 3 bei der Deutschen Bahn mithilfe von JSCodeShift vorstellen.  

Kontext der Transformation

Für Projekte mit großem Volumen und hoher Komplexität kann die manuelle Migration ein zeitaufwendiges Unterfangen sein, das hohe Personalkosten und ein erhebliches Fehlerrisiko mit sich bringt. Vor diesem Hintergrund haben wir uns auf die Suche nach einer effizienteren Lösung als die der manuellen Migration begeben. Das Mittel der Wahl stellte in unserem Kontext JSCodeShift dar.  

Warum JSCodeShift?

JSCodeShift ist ein Open-Source-Toolkit, das auf Recast aufbaut. Es hilft Entwickler:innen dabei, JavaScript-Code über die Manipulation von Abstract-Syntax-Trees* zu refaktorisieren. JSCodeShift erweist sich bei der Handhabung umfangreicher Codebasen als besonders wertvoll: Es ermöglicht Automatisierungen von Codetransformationen, indem es Quellcode analysiert und zielgerichtet neu strukturiert. Entwickelt in einer Node.js-Umgebung, ist es mit einer API ausgestattet, die für die effiziente Manipulation von Abstract-Syntax-Trees optimiert ist.   

Ein für uns relevanter Punkt für die Verwendung von JSCodeShift war die Beibehaltung des eigenen Programmierstils, insbesondere aber der Erhalt von Kommentaren. JSCodeShift fokussiert sich allein auf die Funktionalität des zugrunde liegenden Abstract-Syntax-Trees und übernimmt darüber hinaus sämtlichen implementierten Code.  

In dieser Hinsicht stellt JSCodeShift ein wirksames Tool dar, das die Codeanalyse und -transformation effizient ermöglicht. 

Maßgeschneiderter Converter

Basierend auf JSCodeShift haben wir einen Converter für die Umstellung von VueJS 2 auf VueJS 3 Composition-API entwickelt. Um dem zeitlichen Aspekt Rechnung zu tragen, war unser Ziel, eine automatisierte Transformationsrate von mindestens 80 % des Source-Codes zu erreichen. Wie wir dabei vorgegangen sind, möchten wir anhand einiger Bespiele skizzieren.  

Programmierbeispiel

Im nachfolgenden Abschnitt veranschaulichen wir, wie man mit JSCodeShift die Überführung von Methoden aus der Vue-Options-API in die Syntax für die Composition-API durchführt.  

Als Ausgang für die Vue Options API dient dabei der nachfolgende Code:

input.ts 

export default {   

    methods: {   

        greet(): string {   

            return 'Hello';   

        },   

        greetAsync: async (): Promise<string> => {   

            return await Promise.resolve('Hello');   

        }   

    }   

}  Code-Sprache: JavaScript (javascript)

Die Installation von JSCodeShift wird für die weiteren Schritte vorausgesetzt:   

npm install -g jscodeshift 

Für die Umwandlung benötigen wir ein Transformationsmodul für JSCodeShift. Es dient dazu, die Codeüberarbeitungen durchzuführen. Das API-Objekt ist hierbei von zentraler Bedeutung. Es stellt die JSCodeShift-Bibliothek sowie Hilfsfunktionen für den Runner bereit. Mithilfe der Bibliothek kann der Abstract-Syntax-Tree durchlaufen und transformiert werden. Ferner enthält das Objekt FileInfo wichtige Informationen, darunter Dateipfad und Dateiinhalt:

transformMethods.js 

module.exports = function(fileInfo, api, options) {   

    const {j} = api;   

    const root = j(fileInfo.source);   

   

    // die Transformation findet hier statt   

   

    return root.toSource();   

};   

module.exports.parser = 'babylon'; Code-Sprache: JavaScript (javascript)

Die Methode j(fileInfo.source) stellt den Inhalt der zu konvertierenden Datei in Form von Abstract-Syntax-Tree-Nodes zur Verfügung. Beachtenswert ist, dass verschiedene Parser verwendet werden können, darunter beispielsweise Babel, Babylon, Flow, TypeScript und TSX. Einige dieser Parser sind im AST-Explorer verfügbar. Das erweitert die Flexibilität und Anpassungsfähigkeit des Systems in Bezug auf die Analyse und Transformation von JavaScript-Code erheblich. 

Das zuvor definierte Transformationsmodul kann nun mithilfe von JSCodeShift auf der Datei input.ts angewendet werden:  

jscodeshift -t transformMethods.js input.ts Code-Sprache: CSS (css)

Das Abstract-Syntax-Tree-Objekt ermöglicht es, mithilfe der find-Funktion im Abstract-Syntax-Tree gezielt nach bestimmten Eigenschaften zu suchen. JSCodeShift gibt diese als eine Collection von Node-Paths zurück. Diese Node-Paths repräsentieren Code-Blöcke innerhalb des Abstract-Syntax-Tree. Jeder einzelne Node-Path kann dann individuell durchlaufen, gemäß der spezifischen Anforderungen transformiert und schließlich in den Code eingefügt werden.  

const rootNode = root.find(j.ExportDefaultDeclaration);   

const methodsNodePathCollection = rootNode.find(j.ObjectProperty, {   

    key: {   

        name: 'methods'   

    }   

});   

  

methodsNodePathCollection.forEach(methodNodePath => {   

    // Objekt mit allen definierten Methoden (greet, greetAsync)  

    const value = methodNodePath.node.value;   

   

    // Iteration der einzelnen Methoden   

    value.properties.forEach(method => {   

        // Transformation folgt 

    });   

});   

rootNode.remove(); Code-Sprache: JavaScript (javascript)

Die relevanten Eigenschaften der Funktion werden aus der Abstract-Syntax-Tree-Node extrahiert und der neuen Funktionsdeklaration zugewiesen. Nachfolgend wird die vollständige Node, also die Deklaration, hinter dem alten Code-Block eingefügt.  

const identifier = j.identifier(method.key.name);   

// Unterschied zwischen Function und ArrowFunction  

const isObjectMethod = 'params' in method && 'body' in method && 'async' in method;   

const { params, body, async, comments, returnType } = isObjectMethod ? method : method.value;   

   

const declaration = j.functionDeclaration(   

    identifier,   

    params,   

    body   

);   

declaration.async = async;   

declaration.comments = comments;   

declaration.returnType = returnType;   

   

rootNode.insertAfter(declaration);  Code-Sprache: JavaScript (javascript)

Das transformierte Ergebnis der input.ts sieht dann wie folgt aus:   

function greet(): string {   

    return 'Hello';   

}   

   

async function greet(): Promise<string> {   

    return await Promise.resolve(() => 'Hello');   

}Code-Sprache: JavaScript (javascript)

Weitere Transformationen für die Composition-API und das Beispielprojekt sind unter folgendem Link verfügbar: GitHub. 

Fazit

Zusammenfassend ermöglicht die Verwendung von JSCodeShift eine effiziente und programmatische Änderung von JavaScript-Quellcode. Diese Arbeitsweise erleichtert die Wartbarkeit von Softwareprojekten, speziell Refactorings und notwendige Anpassungen von großen Codebasen. JSCodeShift ist ein nützliches Werkzeug für Entwickler:innen, um zeitaufwendige und fehleranfällige Aufgaben im Zusammenhang mit Codeänderungen zu automatisieren und zu standardisieren.  

Die Einführung unseres Converters hat im Kontext der Umstellung von VueJS 2 zu VueJS 3 zu signifikanten Arbeitserleichterungen und Zeitersparnissen geführt. Es gab einzelne Codefragmente, die manuell transformiert werden mussten, da für Einzelfälle keine Transformationen geschrieben wurden. Aber auch hier hätte mit entsprechendem Aufwand die Möglichkeit zur Erweiterung des Converters für solche Szenarien bestanden.  

Um auch anderen Projekten die Migration von VueJS 2 auf VueJS 3 zu ermöglichen, stellt die Deutsche Bahn den dabei entstandenen Converter als Open-Source zur Verfügung.

Wir hoffen, damit einen Beitrag zur zeitgerechten Migration von VueJS 2 zu VueJS 3 und der Class-Component-Syntax zur Compositon-API zu leisten.  

Was in jedem Fall bei großen Updatemaßnahmen in Projekten hilft, sind eine klare Struktur und Architektur. Wer hierzu mehr wissen möchte oder Hilfe benötigt, sollte sich unser Angebot zu Konzeption einer stabilen Frontend-Architektur anschauen: https://conciso.de/viable-architecture/frontend-architektur-konzeption/

Glossar:

*Ein Abstract-Syntax-Tree (AST) ist eine hierarchische Datenstruktur, die die syntaktische Struktur eines Programmiercodes darstellt. Indem sie dessen Elemente in Form von Knoten und Verbindungen zwischen ihnen organisiert, kann sie für Analysen und Transformationen im Programmcode verwendet werden.  

Das könnte Dich auch noch interessieren

Exploratives Testen

Exploratives Testen

Lernt exploratives Testen und das zugehörige Vorgehen kennen! ...
Titelbild zum Beitrag "Behavior Driven Development und seine Umsetzung mit Cucumber"

Behavior Driven Development und seine Umsetzung mit Cucumber

Tauchen wir gemeinsam in die Welt des Behavior Driven Development ein. Lass uns entdecken, wie diese Methode die Art und ...
Durchstarten mit Keycloak und Docker

Durchstarten mit Keycloak und Docker

In diesem Beitrag erfahren Sie, wie Sie Keycloak mithilfe von Docker ausführen und konfigurieren können. Wir erläutern die Bedeutung von ...