28. Mar 2022Frontend

Jscodeshift: Napíš kód, ktorý prepíše tvoj kód

Zaujal ťa názov tohto článku? Potom je na mieste položiť si otázku, koľko krát ste používali funkciu vývojového prostredia “find and replace”, napríklad na vymazanie volania už nepotrebnej funkcie alebo k iným zmenám v zdrojovom kóde.

Roman HaluškaFrontend developer

Na úpravy veľkého počtu zmien totiž existuje aj rýchlejší spôsob, a to použitie regulárnych výrazov. Má to však aj svoje nevýhody - najmä to, že v prípade zložitejších zmien musíte rozumieť kontextu kódu, a tiež sa vybaviť poriadnou dávkou trpezlivosti a času.

Do hry prichádzajú "codemods"

Čo je to codemod? Codemod je skript, ktorý prepíše iný kód. Funguje to podobne ako pri funkcii  "find and replace", kde transformačný skript dokáže nájsť napríklad hľadanú premennú v kóde a zmeniť jej deklaráciu alebo typ. Taktiež môžete tento skript využiť aj na aktualizáciu kódu v prípade zásadných zmien v knižnici, ktorú používate, ďalej na rozsiahle zmeny pri úprave rozhrania. Codemodes viete použiť v zásade na akékoľvek zmeny v rámci projektu.

V tomto článku ďalej spolu preskúmame možnosti nástroja pre codemods s názvom jscodeshift a predstavíme ukážku jeho reálneho využitia pri migrácii z knižnice moment na dayjs.

Čo je Jscodeshift a ako funguje?

Jscodeshift je nástroj, ktorý spúšťa transformačný skript nad jedným alebo viacerými súbormi JavaScriptu alebo TypeScriptu. Zároveň je to wrapper modulu recast, ktorý rozširuje toto API o ďalšie funkcie. V prípade, že budete potrebovať nejaké bližšie informácie o funkciách, ktoré tento nástroj poskytuje, pozrite si dokumentáciu.  Ak potrebujete radu, určite odporúčam navštíviť stránku jscodeshift komunity.

Jscodeshift prečíta všetky súbory, ktoré mu poskytnete pri spustení a v rámci transformácie analyzuje a preloží zdroj do abstraktného syntaktického stromu "AST", následne hľadá zhody zadané v transformačnom skripte, zhody vymaže, alebo nahradí požadovaným obsahom, a potom znova vygeneruje súbor zo zmeneného AST.

Inštalácia Jscodeshift krok za krokom.

Nainštalujte jscodeshift globálne.

npm i -g jscodeshift

Na úrovni package.json vytvoríme súbor, napríklad s názvom transform.js. V tomto súbore zadefinujeme funkciu transformer. Ako parser použijeme babel, ďalej definujeme API a source file. Nižšie môžme vidieť príklad skriptu, ktorý transformuje node moment na dayjs.

const parser = 'babel'

function transformer (file, api) {
	const j = api.jscodeshift;
	const root = j(file.source);

	return root.find(j.Identifier)
		.forEach(path => {
			// find declaration for "moment" identifier
			if (path.node.name === 'moment') {
				j(path).replaceWith(
					j.identifier('dayjs')
				);
			}
		})
		.toSource();
}

module.exports = transformer
module.exports.parser = parser

Spustenie transformačného skriptu

Príklad spustenia transformačného skriptu transform.js pre adresár src.

jscodeshift ./src --extensions=js,jsx -t transform.js -p

V prípade, že chceme spustiť transformáciu iba nad určitým súborom, tak adresár nahradíme cestou k súboru. Ak sa jedná o typescript, je potrebné zmeniť hodnotu prepínač "extensions" na ts,tsx. Keď je potrebné použiť iný parser, je možné ho zadefinovať cez prepínač

--parser=babel|babylon|flow|ts|tsx , prípadne vlastný parser --parser-config=FILE . Ak chcete, aby transfomácia prebehla, ale nedošlo k zápisu zmien do súborov, je potrebné použiť prepínač d . Bližšie informácie nájdete na Meta GitHube.

Čo je AST - Abstract syntax tree?

AST je syntaktický strom, teda stromová reprezentácia abstraktnej syntaktickej štruktúry zdrojového kódu napísaného vo formálnom jazyku. Každý uzol "node" stromu označuje konštrukt vyskytujúci sa v texte.  Pre vývoj transformačného skriptu odporúčam využívať online nástroj AST explorer.

Jscodeshift API využíva tri typy objektov a to nodes, node-paths, a collections.

Pre ďalšiu prácu s API je potrebné pochopiť túto štruktúru:

  • Node - Node je základný prvok AST, sú to jednoduché objekty, ktoré neposkytujú žiadne metódy.
  • Node-paths/paths - Paths je v podstate adresa nodu v danom strome, ktorá poskytuje cestu v prípade hľadania a identifikáciu nodu, a teda uchováva aj informácie o rodičoch daného nodu. K nodu môžete pristupovať prostredníctvom vlastnosti node.
  • Collections - Je skupina jedného alebo viacerých paths, ktoré jscodeshift API vráti, keď zadáte dotaz na source.

Codemods test driven development pomocou jest

Transformačný skript je možné vyvíjať aj metódou vytvorenia testovacích scenárov, kde si zadefinujeme inputs a outputs, a následne pomocou vyhodnocovania testov vylepšujeme transformačný skript. Jscodeshift je integrovaný pomocou utility na testovanie s frameworkom jest.

Napríklad na úrovni package.json vytvoríme adresár __tests__ a __testfixtures__. V adresári test vytvoríme súbor testCases.js ,kde zadefinujeme test. Druhý parameter funkcie defineTest je názov súboru, kde sa nachádza transformačný skript, v našom prípade transform.js a štvrtý parameter funkcie je názov pripravených testovacích scenárov, v našom prípade s názvom simple. Do adresára __testfixtures__  vytvoríme dva súbory, a to simple.input.js, kde deklarujeme prípad kódu pred spustením skriptu a simple.output.js , kde deklarujeme, ako má vyzerať kód po transformácii.

testCases.js

jest.autoMockOff();
const defineTest = require('jscodeshift/dist/testUtils').defineTest;

defineTest(__dirname, 'transform', null, 'simple');

simple.output.js

import React from 'react'
import dayjs from 'moment'

const page = () => {
   return <div className={'subtitle'}>{dayjs(publishedAt).format(t('dateFormat'))}</div>;
}

Testy spúšťame pomocou príkazu jest .

Príklad transformačného skriptu pre deklaráciu importu modulu moment:

const parser = 'babel'

function transformer (file, api) {
   const j = api.jscodeshift;
   const root = j(file.source);

   // find declaration for "moment" import
   return root.find(j.ImportDeclaration, { source: { value: 'moment'}}).forEach(path => {
	   path.value.source.value = 'dayjs'
   }).toSource();
}

module.exports = transformer
module.exports.parser = parser

Zhrnutie

Dúfam, že vám tento článok priblížil možnosti nástroja jscodeshift, ktorý poskytuje dostatočnú flexibilitu pri akejkoľvek automatickej úprave kódu. Umožňuje zložité transformácie kódu v priebehu niekoľkých sekúnd. Keď sa časom zorientujete a stanete zručnejšími v písaní transformačných skriptov, zvýši si vaša, ale aj tímová efektivita v riešení problému popísaného v tomto článku.

Roman HaluškaFrontend developer