Scrivi il codice per riscrivere il tuo codice: jscodeshift

Pubblicato: 2022-03-11

Codemod con jscodeshift

Quante volte hai utilizzato la funzionalità trova e sostituisci in una directory per apportare modifiche ai file di origine JavaScript? Se sei bravo, sei diventato fantasioso e hai usato espressioni regolari con l'acquisizione di gruppi, perché vale la pena se la tua base di codice è considerevole. Regex ha dei limiti, però. Per modifiche non banali, è necessario uno sviluppatore che comprenda il codice nel contesto e sia anche disposto ad affrontare il processo lungo, noioso e soggetto a errori.

È qui che entrano in gioco i "codemod".

I codemod sono script utilizzati per riscrivere altri script. Considerali come una funzionalità di ricerca e sostituzione in grado di leggere e scrivere codice. Puoi usarli per aggiornare il codice sorgente per adattarlo alle convenzioni di codifica di un team, apportare modifiche diffuse quando un'API viene modificata o persino correggere automaticamente il codice esistente quando il tuo pacchetto pubblico apporta una modifica sostanziale.

Il toolkit jscodeshift è ottimo per lavorare con i codemod.

Pensa ai codemod come a una funzionalità di ricerca e sostituzione con script in grado di leggere e scrivere codice.
Twitta

In questo articolo, esploreremo un toolkit per codemod chiamato "jscodeshift" mentre creeremo tre codemod di complessità crescente. Alla fine avrai un'ampia esposizione agli aspetti importanti di jscodeshift e sarai pronto per iniziare a scrivere i tuoi codemod. Esamineremo tre esercizi che coprono alcuni usi di base, ma fantastici, dei codemod e puoi visualizzare il codice sorgente per questi esercizi sul mio progetto github.

Cos'è jscodeshift?

Il toolkit jscodeshift ti consente di pompare un sacco di file sorgente attraverso una trasformazione e sostituirli con ciò che esce dall'altra parte. All'interno della trasformazione, analizzi la sorgente in un albero della sintassi astratto (AST), dai un'occhiata per apportare le modifiche, quindi rigenera la sorgente dall'AST alterato.

L'interfaccia fornita da jscodeshift è un wrapper per i pacchetti recast e ast-types . recast gestisce la conversione dall'origine a AST e viceversa mentre ast-types gestisce l'interazione di basso livello con i nodi AST.

Impostare

Per iniziare, installa jscodeshift a livello globale da npm.

 npm i -g jscodeshift

Ci sono opzioni per i corridori che puoi usare e una configurazione di test supponente che rende l'esecuzione di una suite di test tramite Jest (un framework di test JavaScript open source) davvero facile, ma per ora lo ignoreremo a favore della semplicità:

jscodeshift -t some-transform.js input-file.js -d -p

Questo eseguirà input-file.js attraverso la trasformazione some-transform.js e stamperà i risultati senza alterare il file.

Prima di iniziare, tuttavia, è importante comprendere tre tipi di oggetti principali con cui si occupa l'API jscodeshift: nodi, percorsi di nodi e raccolte.

Nodi

I nodi sono gli elementi costitutivi di base dell'AST, spesso indicati come "nodi AST". Questi sono ciò che vedi quando esplori il tuo codice con AST Explorer. Sono oggetti semplici e non forniscono alcun metodo.

Nodi-percorsi

I percorsi dei nodi sono wrapper attorno a un nodo AST fornito da ast-types come un modo per attraversare l'albero della sintassi astratta (AST, ricordi?). In isolamento, i nodi non hanno alcuna informazione sul loro genitore o ambito, quindi i percorsi dei nodi si occupano di questo. È possibile accedere al nodo avvolto tramite la proprietà node e sono disponibili diversi metodi per modificare il nodo sottostante. i percorsi dei nodi vengono spesso definiti semplicemente "percorsi".

Collezioni

Le raccolte sono gruppi di zero o più percorsi di nodo che l'API jscodeshift restituisce quando si interroga l'AST. Hanno tutti i tipi di metodi utili, alcuni dei quali esploreremo.

Le raccolte contengono percorsi dei nodi, i percorsi dei nodi contengono nodi e i nodi sono ciò di cui è fatto l'AST. Tienilo a mente e sarà facile capire l'API di query jscodeshift.

Può essere difficile tenere traccia delle differenze tra questi oggetti e le loro rispettive capacità API, quindi c'è uno strumento elegante chiamato jscodeshift-helper che registra il tipo di oggetto e fornisce altre informazioni chiave.

È importante conoscere la differenza tra nodi, percorsi dei nodi e raccolte.

È importante conoscere la differenza tra nodi, percorsi dei nodi e raccolte.

Esercizio 1: Rimuovere le chiamate alla console

Per bagnarci i piedi, iniziamo con la rimozione delle chiamate a tutti i metodi della console nella nostra base di codice. Anche se puoi farlo con trova e sostituisci e una piccola espressione regolare, inizia a diventare complicato con istruzioni multilinea, valori letterali modello e chiamate più complesse, quindi è un esempio ideale per iniziare.

Innanzitutto, crea due file, remove-consoles.js e remove-consoles.input.js :

 //remove-consoles.js export default (fileInfo, api) => { };
 //remove-consoles.input.js export const sum = (a, b) => { console.log('calling sum with', arguments); return a + b; }; export const multiply = (a, b) => { console.warn('calling multiply with', arguments); return a * b; }; export const divide = (a, b) => { console.error(`calling divide with ${ arguments }`); return a / b; }; export const average = (a, b) => { console.log('calling average with ' + arguments); return divide(sum(a, b), 2); };

Ecco il comando che useremo nel terminale per inviarlo tramite jscodeshift:

jscodeshift -t remove-consoles.js remove-consoles.input.js -d -p

Se tutto è impostato correttamente, quando lo esegui dovresti vedere qualcosa del genere.

 Processing 1 files... Spawning 1 workers... Running in dry mode, no files will be written! Sending 1 files to free worker... All done. Results: 0 errors 0 unmodified 1 skipped 0 ok Time elapsed: 0.514seconds

OK, è stato un po' deludente poiché la nostra trasformazione in realtà non fa ancora nulla, ma almeno sappiamo che funziona tutto. Se non funziona affatto, assicurati di aver installato jscodeshift a livello globale. Se il comando per eseguire la trasformazione non è corretto, vedrai un messaggio "ERRORE File di trasformazione … non esiste" o "TypeError: il percorso deve essere una stringa o un buffer" se non è possibile trovare il file di input. Se hai dito grasso qualcosa, dovrebbe essere facile da individuare con gli errori di trasformazione molto descrittivi.

Correlati: cheat sheet JavaScript rapido e pratico di Toptal: ES6 e oltre

Il nostro obiettivo finale, tuttavia, dopo una trasformazione riuscita, è vedere questa fonte:

 export const sum = (a, b) => { return a + b; }; export const multiply = (a, b) => { return a * b; }; export const divide = (a, b) => { return a / b; }; export const average = (a, b) => { return divide(sum(a, b), 2); };

Per arrivarci, dobbiamo convertire la sorgente in un AST, trovare le console, rimuoverle e quindi riconvertire l'AST modificato in sorgente. Il primo e l'ultimo passaggio sono facili, è solo:

 remove-consoles.js export default (fileInfo, api) => { const j = api.jscodeshift; const root = j(fileInfo.source); return root.toSource(); };

Ma come troviamo le console e le rimuoviamo? A meno che tu non abbia una conoscenza eccezionale dell'API di Mozilla Parser, probabilmente avrai bisogno di uno strumento per aiutarti a capire che aspetto ha l'AST. Per questo puoi usare AST Explorer. Incolla il contenuto di remove-consoles.input.js e vedrai l'AST. Ci sono molti dati anche nel codice più semplice, quindi aiuta a nascondere i dati e i metodi di posizione. È possibile attivare o disattivare la visibilità delle proprietà in AST Explorer con le caselle di controllo sopra l'albero.

Possiamo vedere che le chiamate ai metodi della console sono denominate CallExpressions , quindi come possiamo trovarle nella nostra trasformazione? Usiamo le query di jscodeshift, ricordando la nostra precedente discussione sulle differenze tra raccolte, percorsi di nodo e nodi stessi:

 //remove-consoles.js export default (fileInfo, api) => { const j = api.jscodeshift; const root = j(fileInfo.source); return root.toSource(); };

La riga const root = j(fileInfo.source); restituisce una raccolta di un percorso di nodo, che esegue il wrapping del nodo AST radice. Possiamo usare il metodo di find della raccolta per cercare i nodi discendenti di un certo tipo, in questo modo:

 const callExpressions = root.find(j.CallExpression);

Ciò restituisce un'altra raccolta di percorsi di nodi contenenti solo i nodi che sono CallExpressions. A prima vista, questo sembra quello che vogliamo, ma è troppo ampio. Potremmo finire per eseguire centinaia o migliaia di file attraverso le nostre trasformazioni, quindi dobbiamo essere precisi per avere la certezza che funzionerà come previsto. L'ingenuità di cui sopra non find solo la console CallExpressions, ma troverebbe ogni CallExpression nel sorgente, incluso

 require('foo') bar() setTimeout(() => {}, 0)

Per forzare una maggiore specificità, forniamo un secondo argomento a .find : un oggetto di parametri aggiuntivi, ogni nodo deve essere incluso nei risultati. Possiamo guardare AST Explorer per vedere che le nostre chiamate console.* hanno la forma di:

 { "type": "CallExpression", "callee": { "type": "MemberExpression", "object": { "type": "Identifier", "name": "console" } } }

Con questa conoscenza, sappiamo di perfezionare la nostra query con uno specificatore che restituirà solo il tipo di CallExpressions a cui siamo interessati:

 const callExpressions = root.find(j.CallExpression, { callee: { type: 'MemberExpression', object: { type: 'Identifier', name: 'console' }, }, });

Ora che abbiamo una raccolta accurata dei siti di chiamata, rimuoviamoli dall'AST. Convenientemente, il tipo di oggetto di raccolta ha un metodo di remove che farà proprio questo. Il nostro file remove-consoles.js ora apparirà così:

 //remove-consoles.js export default (fileInfo, api) => { const j = api.jscodeshift; const root = j(fileInfo.source) const callExpressions = root.find(j.CallExpression, { callee: { type: 'MemberExpression', object: { type: 'Identifier', name: 'console' }, }, } ); callExpressions.remove(); return root.toSource(); };

Ora, se eseguiamo la nostra trasformazione dalla riga di comando usando jscodeshift -t remove-consoles.js remove-consoles.input.js -d -p , dovremmo vedere:

 Processing 1 files... Spawning 1 workers... Running in dry mode, no files will be written! Sending 1 files to free worker... export const sum = (a, b) => { return a + b; }; export const multiply = (a, b) => { return a * b; }; export const divide = (a, b) => { return a / b; }; export const average = (a, b) => { return divide(sum(a, b), 2); }; All done. Results: 0 errors 0 unmodified 0 skipped 1 ok Time elapsed: 0.604seconds

Sembra buona. Ora che la nostra trasformazione altera l'AST sottostante, l'utilizzo .toSource() genera una stringa diversa dall'originale. L'opzione -p del nostro comando mostra il risultato e in basso viene mostrato un conteggio delle disposizioni per ogni file elaborato. La rimozione dell'opzione -d dal nostro comando sostituirebbe il contenuto di remove-consoles.input.js con l'output della trasformazione.

Il nostro primo esercizio è completo... quasi. Il codice ha un aspetto bizzarro e probabilmente molto offensivo per tutti i puristi funzionali là fuori, quindi per migliorare il flusso del codice di trasformazione, jscodeshift ha reso la maggior parte delle cose concatenabili. Questo ci permette di riscrivere la nostra trasformazione in questo modo:

 // remove-consoles.js export default (fileInfo, api) => { const j = api.jscodeshift; return j(fileInfo.source) .find(j.CallExpression, { callee: { type: 'MemberExpression', object: { type: 'Identifier', name: 'console' }, }, } ) .remove() .toSource(); };

Molto meglio. Per ricapitolare l'esercizio 1, abbiamo eseguito il wrapping della sorgente, richiesto una raccolta di percorsi dei nodi, modificato l'AST e quindi rigenerato tale sorgente. Ci siamo bagnati i piedi con un esempio piuttosto semplice e abbiamo toccato gli aspetti più importanti. Ora, facciamo qualcosa di più interessante.

Esercizio 2: Sostituzione delle chiamate di metodo importate

Per questo scenario, abbiamo un modulo "geometry" con un metodo chiamato "circleArea" che abbiamo deprecato a favore di "getCircleArea". Potremmo facilmente trovarli e sostituirli con /geometry\.circleArea/g , ma cosa succede se l'utente ha importato il modulo e gli ha assegnato un nome diverso? Per esempio:

 import g from 'geometry'; const area = g.circleArea(radius);

Come sapremmo sostituire g.circleArea invece di geometry.circleArea ? Non possiamo certo presumere che tutte le chiamate circleArea siano quelle che stiamo cercando, abbiamo bisogno di un contesto. È qui che i codemod iniziano a mostrare il loro valore. Iniziamo creando due file, deprecated.js e deprecated.input.js .

 //deprecated.js export default (fileInfo, api) => { const j = api.jscodeshift; const root = j(fileInfo.source); return root.toSource(); };
 deprecated.input.js import g from 'geometry'; import otherModule from 'otherModule'; const radius = 20; const area = g.circleArea(radius); console.log(area === Math.pow(g.getPi(), 2) * radius); console.log(area === otherModule.circleArea(radius));

Ora esegui questo comando per eseguire il codemod.

jscodeshift -t ./deprecated.js ./deprecated.input.js -d -p

Dovresti vedere l'output che indica che la trasformazione è stata eseguita, ma non ha ancora cambiato nulla.

 Processing 1 files... Spawning 1 workers... Running in dry mode, no files will be written! Sending 1 files to free worker... All done. Results: 0 errors 1 unmodified 0 skipped 0 ok Time elapsed: 0.892seconds

Abbiamo bisogno di sapere come è stato importato il nostro modulo di geometry . Diamo un'occhiata all'AST Explorer e scopriamo cosa stiamo cercando. La nostra importazione assume questa forma.

 { "type": "ImportDeclaration", "specifiers": [ { "type": "ImportDefaultSpecifier", "local": { "type": "Identifier", "name": "g" } } ], "source": { "type": "Literal", "value": "geometry" } }

Possiamo specificare un tipo di oggetto per trovare una raccolta di nodi come questa:

 const importDeclaration = root.find(j.ImportDeclaration, { source: { type: 'Literal', value: 'geometry', }, });

Questo ci ottiene la ImportDeclaration utilizzata per importare la "geometria". Da lì, scorri verso il basso per trovare il nome locale utilizzato per contenere il modulo importato. Poiché questa è la prima volta che lo facciamo, segnaliamo un punto importante e confuso quando iniziamo per la prima volta.

Nota: è importante sapere che root.find() restituisce una raccolta di percorsi dei nodi. Da lì, il metodo .get(n) restituisce il percorso del nodo all'indice n in quella raccolta e, per ottenere il nodo effettivo, utilizziamo .node . Il nodo è fondamentalmente ciò che vediamo in AST Explorer. Ricorda, il percorso del nodo è principalmente informazioni sull'ambito e sulle relazioni del nodo, non sul nodo stesso.

 // find the Identifiers const identifierCollection = importDeclaration.find(j.Identifier); // get the first NodePath from the Collection const nodePath = identifierCollection.get(0); // get the Node in the NodePath and grab its "name" const localName = nodePath.node.name;

Questo ci permette di capire dinamicamente come è stato importato il nostro modulo di geometry . Successivamente, troviamo i luoghi in cui viene utilizzato e li cambiamo. Osservando AST Explorer, possiamo vedere che dobbiamo trovare MemberExpressions che assomigliano a questo:

 { "type": "MemberExpression", "object": { "name": "geometry" }, "property": { "name": "circleArea" } }

Ricorda, tuttavia, che il nostro modulo potrebbe essere stato importato con un nome diverso, quindi dobbiamo tenerne conto facendo invece la nostra query simile a questa:

 j.MemberExpression, { object: { name: localName, }, property: { name: "circleArea", }, })

Ora che abbiamo una query, possiamo ottenere una raccolta di tutti i siti di chiamata al nostro vecchio metodo e quindi utilizzare il metodo replaceWith() della raccolta per scambiarli. Il metodo replaceWith() scorre la raccolta, passando ogni percorso del nodo a una funzione di callback. Il nodo AST viene quindi sostituito con qualsiasi nodo restituito dalla richiamata.

I codemod ti consentono di scrivere considerazioni "intelligenti" per il refactoring.

Ancora una volta, è necessario comprendere la differenza tra raccolte, percorsi dei nodi e nodi affinché ciò abbia un senso.

Una volta terminata la sostituzione, generiamo il sorgente come al solito. Ecco la nostra trasformazione finita:

 //deprecated.js export default (fileInfo, api) => { const j = api.jscodeshift; const root = j(fileInfo.source); // find declaration for "geometry" import const importDeclaration = root.find(j.ImportDeclaration, { source: { type: 'Literal', value: 'geometry', }, }); // get the local name for the imported module const localName = // find the Identifiers importDeclaration.find(j.Identifier) // get the first NodePath from the Collection .get(0) // get the Node in the NodePath and grab its "name" .node.name; return root.find(j.MemberExpression, { object: { name: localName, }, property: { name: 'circleArea', }, }) .replaceWith(nodePath => { // get the underlying Node const { node } = nodePath; // change to our new prop node.property.name = 'getCircleArea'; // replaceWith should return a Node, not a NodePath return node; }) .toSource(); };

Quando eseguiamo il sorgente attraverso la trasformazione, vediamo che la chiamata al metodo deprecato nel modulo geometry è stata modificata, ma il resto è rimasto inalterato, in questo modo:

 import g from 'geometry'; import otherModule from 'otherModule'; const radius = 20; const area = g.getCircleArea(radius); console.log(area === Math.pow(g.getPi(), 2) * radius); console.log(area === otherModule.circleArea(radius));

Esercizio 3: Modifica della firma di un metodo

Negli esercizi precedenti abbiamo trattato l'interrogazione di raccolte per tipi specifici di nodi, la rimozione di nodi e la modifica dei nodi, ma per quanto riguarda la creazione di nodi completamente nuovi? Questo è ciò che affronteremo in questo esercizio.

In questo scenario, abbiamo una firma del metodo che è andata fuori controllo con i singoli argomenti man mano che il software è cresciuto, quindi è stato deciso che sarebbe stato meglio accettare invece un oggetto contenente quegli argomenti.

Invece di car.factory('white', 'Kia', 'Sorento', 2010, 50000, null, true);

vorremmo vedere

 const suv = car.factory({ color: 'white', make: 'Kia', model: 'Sorento', year: 2010, miles: 50000, bedliner: null, alarm: true, });

Iniziamo realizzando la trasformazione e un file di input con cui testare:

 //signature-change.js export default (fileInfo, api) => { const j = api.jscodeshift; const root = j(fileInfo.source); return root.toSource(); };
 //signature-change.input.js import car from 'car'; const suv = car.factory('white', 'Kia', 'Sorento', 2010, 50000, null, true); const truck = car.factory('silver', 'Toyota', 'Tacoma', 2006, 100000, true, true);

Il nostro comando per eseguire la trasformazione sarà jscodeshift -t signature-change.js signature-change.input.js -d -p e i passaggi necessari per eseguire questa trasformazione sono:

  • Trova il nome locale per il modulo importato
  • Trova tutti i siti di chiamata al metodo .factory
  • Leggi tutti gli argomenti passati
  • Sostituisci quella chiamata con un singolo argomento che contiene un oggetto con i valori originali

Utilizzando AST Explorer e il processo che abbiamo utilizzato negli esercizi precedenti, i primi due passaggi sono facili:

 //signature-change.js export default (fileInfo, api) => { const j = api.jscodeshift; const root = j(fileInfo.source); // find declaration for "car" import const importDeclaration = root.find(j.ImportDeclaration, { source: { type: 'Literal', value: 'car', }, }); // get the local name for the imported module const localName = importDeclaration.find(j.Identifier) .get(0) .node.name; // find where `.factory` is being called return root.find(j.CallExpression, { callee: { type: 'MemberExpression', object: { name: localName, }, property: { name: 'factory', }, } }) .toSource(); };

Per leggere tutti gli argomenti attualmente passati, utilizziamo il metodo replaceWith() sulla nostra raccolta di CallExpressions per scambiare ciascuno dei nodi. I nuovi nodi sostituiranno node.arguments con un nuovo argomento singolo, un oggetto.

Scambia facilmente gli argomenti del metodo con jscodeshift!

Cambia le firme del metodo con 'replacewith()' e scambia interi nodi.

Proviamo con un semplice oggetto per assicurarci di sapere come funziona prima di utilizzare i valori corretti:

 .replaceWith(nodePath => { const { node } = nodePath; node.arguments = [{ foo: 'bar' }]; return node; })

Quando eseguiamo questo ( jscodeshift -t signature-change.js signature-change.input.js -d -p ), la trasformazione esploderà con:

 ERR signature-change.input.js Transformation error Error: {foo: bar} does not match type Printable

Si scopre che non possiamo semplicemente inserire oggetti semplici nei nostri nodi AST. Invece, dobbiamo usare i builder per creare nodi appropriati.

Correlati: assumi il 3% dei migliori sviluppatori Javascript freelance.

Costruttori di nodi

I costruttori ci consentono di creare correttamente nuovi nodi; sono forniti da ast-types e sono emersi tramite jscodeshift. Controllano rigidamente che i diversi tipi di nodi siano creati correttamente, il che può essere frustrante quando si esegue l'hacking su un rotolo, ma alla fine questa è una buona cosa. Per capire come usare i builder, ci sono due cose che dovresti tenere a mente:

Tutti i tipi di nodo AST disponibili sono definiti nella cartella def del progetto github ast-types, principalmente in core.js Ci sono builder per tutti i tipi di nodo AST, ma usano la versione camel-cased del tipo di nodo, non pascal -Astuccio. (Questo non è dichiarato esplicitamente, ma puoi vedere che questo è il caso nella fonte ast-types

Se utilizziamo AST Explorer con un esempio di quello che vogliamo che sia il risultato, possiamo metterlo insieme abbastanza facilmente. Nel nostro caso, vogliamo che il nuovo singolo argomento sia un ObjectExpression con un gruppo di proprietà. Osservando le definizioni di tipo sopra menzionate, possiamo vedere cosa questo comporta:

 def("ObjectExpression") .bases("Expression") .build("properties") .field("properties", [def("Property")]); def("Property") .bases("Node") .build("kind", "key", "value") .field("kind", or("init", "get", "set")) .field("key", or(def("Literal"), def("Identifier"))) .field("value", def("Expression"));

Quindi, il codice per creare un nodo AST per { foo: 'bar' } sarebbe simile a:

 j.objectExpression([ j.property( 'init', j.identifier('foo'), j.literal('bar') ) ]);

Prendi quel codice e collegalo alla nostra trasformazione in questo modo:

 .replaceWith(nodePath => { const { node } = nodePath; const object = j.objectExpression([ j.property( 'init', j.identifier('foo'), j.literal('bar') ) ]); node.arguments = [object]; return node; })

L'esecuzione di questo ci porta il risultato:

 import car from 'car'; const suv = car.factory({ foo: "bar" }); const truck = car.factory({ foo: "bar" });

Ora che sappiamo come creare un nodo AST appropriato, è facile scorrere i vecchi argomenti e generare invece un nuovo oggetto da usare. Ecco come appare ora il nostro file signature-change.js :

 //signature-change.js export default (fileInfo, api) => { const j = api.jscodeshift; const root = j(fileInfo.source); // find declaration for "car" import const importDeclaration = root.find(j.ImportDeclaration, { source: { type: 'Literal', value: 'car', }, }); // get the local name for the imported module const localName = importDeclaration.find(j.Identifier) .get(0) .node.name; // current order of arguments const argKeys = [ 'color', 'make', 'model', 'year', 'miles', 'bedliner', 'alarm', ]; // find where `.factory` is being called return root.find(j.CallExpression, { callee: { type: 'MemberExpression', object: { name: localName, }, property: { name: 'factory', }, } }) .replaceWith(nodePath => { const { node } = nodePath; // use a builder to create the ObjectExpression const argumentsAsObject = j.objectExpression( // map the arguments to an Array of Property Nodes node.arguments.map((arg, i) => j.property( 'init', j.identifier(argKeys[i]), j.literal(arg.value) ) ) ); // replace the arguments with our new ObjectExpression node.arguments = [argumentsAsObject]; return node; }) // specify print options for recast .toSource({ quote: 'single', trailingComma: true }); };

Esegui la trasformazione ( jscodeshift -t signature-change.js signature-change.input.js -d -p ) e vedremo che le firme sono state aggiornate come previsto:

 import car from 'car'; const suv = car.factory({ color: 'white', make: 'Kia', model: 'Sorento', year: 2010, miles: 50000, bedliner: null, alarm: true, }); const truck = car.factory({ color: 'silver', make: 'Toyota', model: 'Tacoma', year: 2006, miles: 100000, bedliner: true, alarm: true, });

Codemods con jscodeshift Riepilogo

Ci sono voluti un po' di tempo e sforzi per arrivare a questo punto, ma i vantaggi sono enormi di fronte al refactoring di massa. Distribuire gruppi di file a processi diversi ed eseguirli in parallelo è qualcosa in cui jscodeshift eccelle, consentendoti di eseguire trasformazioni complesse su un'enorme base di codice in pochi secondi. Man mano che diventi più esperto con i codemod, inizierai a riproporre gli script esistenti (come il repository github react-codemod o a scriverne uno per tutti i tipi di attività, e ciò renderà te, il tuo team e gli utenti dei pacchetti più efficienti .