精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

使用Jscodeshift做自動化重構

原創 精選
開發
在這篇文章中,我們從一個簡化了的實際例子出發,描述了為何jscodeshift在某些場景下可以提供的幫助,比如降低大型修改可能帶來的影響。

作者 | 邱俊濤

在這篇文章里我想要通過一些小例子來介紹使用jscodeshift來進行自動化重構的技術。具體來說,我想要介紹在一個組件庫的開發和維護過程中,如何使用jscodeshift來自動修改公開的API接口,從而盡可能小的產生對組件用戶的影響。

如果你們團隊開發的組件被其消費者(組織內部或者外部)使用了,而這些代碼又不在你的控制之內,那么這里討論的技術和模式可能對你很有幫助。而如果你的日常工作更多的是使用組件庫來開發應用程序,我希望這里的知識和技巧仍然對你有所啟發,畢竟在軟件系統中,我們往往都既是某些庫的消費者,又同時是另外一些庫的生產者。

從一個簡單場景出發

設想這樣一個場景,你發布了一個酷炫的組件庫(fancylib),其中有一個按鈕(Button)組件。這個Button的一個屬性是當點擊后處于加載中(loading)狀態時現實一個表示加載中的小圖標。

(圖片來源:https://xd.adobe.com/ideas/process/ui-design/designing-interactive-buttons-states/)

在代碼實現中,這個加載中狀態被定義為了名為isInLoadingStatus公開prop。用戶可以通過設置其值來控制Button的狀態:

import Button from '@fancylib/button';

const app = () => (
<Button isInLoadingStatus>Click me</Button>
)

一個實習生在某一天code review的時候提出了一個問題:在組件庫中的其他地方,所有的boolean狀態都是用一個單詞來表示的,比如checked, disabled等。如果按照這個慣例,這里應該把isInLoadingStatus簡化為loading。好主意!

import Button from '@fancylib/button';

const app = () => (
<Button loading>Click me</Button>
)

假如所有用到Button的地方都在你的控制之內,字符串替換大約是一個快速且80%有效的方案。不過稍微分析一下,你就會發現簡單的Shift+F6會遇到很多問題。

復雜情況

比如用戶對其做了二次包裝以適配更符合自己用戶的使用習慣,這使得簡單的全局字符串替換變成了不可能::

import Button as FancyButton from '@fancylib/button';

const MyEvenFancierButton = (props: FancyButtonProps) => (
const theme = {
backgroundColor: "orangered",
color: "white"
};
<FancyButton {...props} theme={theme}>Click me</FancyButton>
);

除了這些問題之外,由于這是一個非常受歡迎的組件庫,Button在很多(包括內部和外部的)產品中都有使用,你沒有辦法訪問所有的用戶代碼,更沒有辦法讓所有人都用手工的查找替換來做更新,你需要另尋出路。

你需要一個工具 -- 一個可以讀懂代碼意圖的工具 -- 來幫助你做修改,而且整個過程最好可以自動化,比如通過執行一個腳本來完成。

使用jscodeshift

jscodeshift就是這樣一個工具(工具集)。簡單來說,jscodeshift的工作方式就是將源代碼分析成一棵樹(抽象語法樹),然后提供API來修改這棵樹,最后再把樹生成為代碼。

也就是說,她可以讀懂你的代碼,并提供指令(API)來根據你的意愿修改相應的代碼。

實現

接下來,我們可以通過實現一個可以完成上述場景的自動重構的腳本來對jscodeshift的使用做一個簡單介紹。簡單來說,jscodeshift的工作流程是:首先你需要定義一個轉換腳本(transform),這個腳本需要符合一定的規范以便jscodeshift調用;然后jscodeshift的命令行工具會啟動runner,并將轉換腳本應用到某個文件或者某個文件夾中的所有文件中:

jscodeshift -t myTransform src

定義一個transform

也就是說,我們所有的邏輯都會定義在轉換腳本中。transform腳本需要導出一個固定格式的函數:

import { Transform } from "jscodeshift";

const transform: Transform = (file, api, options) => {
//...
};

export default transform;

file為解析后的文件對象,api是jscodeshift的API對象,可以通過它來查找,修改文件對象,options是一個可選的,用來傳遞其他參數(比如格式化最終輸出格式等)的對象。在函數體中,我們可以使用jscodeshift提供的API來操縱抽象語法樹(Abstract Syntax Tree)來實現對代碼的修改。這個過程和通過DOM API來操作瀏覽器中的頁面元素非常類似:按照屬性查找元素,對查找結果進行增刪改等操作,只不過這里的操作對象是語法樹(比如變量定義,函數體,條件語句等等)。

在詳細討論如何使用jscodeshift的API來修改代碼之前,我們來略微看一下抽象語法樹的概念。這將是我們腳本需要操作的主要對象。

抽象語法樹AST

抽象語法樹,是編譯器將源碼解析(parse)之后形成的一課樹形結構。簡單來說,我們的代碼被解析成為Token,Token再根據語法規則形成子樹,子樹最終根據文法歸并成一顆樹。我們可以通過AST Explorer工具來實時查看代碼對應的語法樹。

舉個例子,我們的代碼片段:

import Button from '@fancylib/button';

const app = () => (
<Button isInLoadingStatus>Click me</Button>
)

經過解析(jscodeshift默認使用babel來解析,你可以選擇其他的解析器)之后,會形成右側的一顆樹,比如isInLoadingStatus被識別成JSXIdentifier類型,而變量app定義則被識別為VariableDeclarator等。所有符合語法的元素都會被抽取成Token,并體現為樹上的一個節點。

有了這些基本概念之后,我們就可以開始編寫一個簡單的transform了。這里我們可以通過AST Explorer提供的在線IDE中的Transform功能來實時調試(此處選擇jscodeshift作為轉換器)。

然后我們定義這樣一個轉換函數:

// Press ctrl+space for code completion
export default function transformer(file, api) {
const j = api.jscodeshift;

return j(file.source)
.find(j.JSXIdentifier)
.forEach(path => {
if(path.node.name === "isInLoadingStatus") {
j(path).replaceWith(
j.identifier('loading')
)
}
})
.toSource();
}

比如上述代碼中,我們查找所有的j.JSXIdentifier,并迭代每一個找到的節點,如果它的值是isInLoadingStatus的話,就將其替換為loading。可以觀察到右下側的調試器窗口中的轉換結果:

測試驅動開發

當然了,作為一個嚴肅的程序員,我們不應該通過一個在線IDE來進行開發。幸運的是jscodeshift可以和jest完美配合,同時我發現編寫自動化腳本是一個非常適合測試驅動開發的場景:

  • 輸入輸出都非常明確
  • 各種不同的邊界場景很容易想象/編寫成用例
  • 每一個步驟都可以劃分的比較小

jscodeshift提供了一個小工具defineInlineTest,通過它你可以很方便的定義測試用例:

import { defineInlineTest } from 'jscodeshift/dist/testUtils';
import transformer from './transformer';

describe('transformer', () => {
defineInlineTest(
{ default: transformer, parser: 'tsx' },
{},
`
import Button from '@fancylib/button';

export default () => (
<Button isInLoadingStatus>Click me</Button>
);
`,
`
import Button from '@fancylib/button';

export default () => (
<Button loading>Click me</Button>
);
`,
'change isInLoadingStatus to loading'
);
});

當然,如果你不習慣字符串模板的話,它同時還提供了基于文件形式的測試定義,這樣你可以將測試的輸入(轉化前)和輸出(轉化后)外置到文件中,并在其中構建較為復雜的使用場景。

比如我們希望這個transform不要誤傷我們代碼中使用的其他Button,比如我們使用了另外一個組件庫,而巧合的是那個庫中Button也有一個isInLoadingStatus。

那么對應的測試用例會是:

   defineInlineTest(
{ default: transformer, parser: 'tsx' },
{},
`
import Button from '@facebook/button';

export default () => (
<Button isInLoadingStatus>Click me</Button>
);
`,
`
import Button from '@facebook/button';

export default () => (
<Button isInLoadingStatus>Click me</Button>
);
`,
'should not change isInLoadingStatus to loading from other package'
);

對應的我們需要在代碼中加入相應的邏輯:

// Press ctrl+space for code completion
export default function transformer(file, api) {
const j = api.jscodeshift;
const root = j(file.source);

const specifiers = root
.find(j.ImportDeclaration)
.filter((path) => path.node.source.value === "@fancylib/button")
.find(j.ImportDefaultSpecifier);

if (specifiers.length === 0) {
return;
}

//...
}

即,我們先查找所有的import語句,如果沒有找到從@fancylib/button導入的Button就跳過后續的操作。你應該已經注意到了,我們這里又很多的諸如j.ImportDeclaration和j.ImportDefaultSpecifier之類的Token定義,你可以從AST Explorer的樹結構中找到類似的名稱,然后用jscodeshift的API來查找并訪問改節點。

這個過程或多或少有點像我們通過DOM的API來選擇HTML節點一樣:

document.querySelectorAll('a')
.filter(anchor => anchor.classList.includes('button'))
.forEach(anchor => anchor.style["text-decoration"] = "underline")

如果你覺得這里要素太多,這是很正常的。嘗試著多寫幾個就會發現規律。

如果把所有的實現細節都列舉在一篇文章中,我覺得文章會非常枯燥(可能寫成一個系列教程等),因此這里我不再貼代碼,相關的源碼可以在https://github.com/abruzzi/codemod-demo找到。

可能的陷阱

使用腳本來自動化重構的想法當然非常有誘惑了,特別是對于疲于為已經公布的API打補丁的人們來說,簡直太過于美好。不過公平起見,我還是得略微說一些它的一些drawbacks。

首先,jscodeshift 的API略顯晦澀,有一定的學習成本。開發過程中可能會有很多調試的工作。其次,它并不定覆蓋100%的使用場景,比如對于復雜的spreading操作,需要調試和分析的工作量不容小覷,也就是說你仍然需要人工校對一些edge cases。最后,需要一些腳本來支持組件的消費團隊使用,比如自動化補丁工具等,如果有多個transform,如何一次patch等問題。

小結

在這篇文章中,我們從一個簡化了的實際例子出發,描述了為何jscodeshift在某些場景下可以提供的幫助,比如降低大型修改可能帶來的影響(而如果影響不可避免,那么如何使其變得不那么痛苦)。隨后我們描述了jscodeshift中的一些基本概念和基本的工作方式,并結合之前討論的例子實現了部分的自動化重構。

責任編輯:趙寧寧 來源: Thoughtworks洞見
相關推薦

2020-12-08 06:20:49

前端重構Vue

2021-06-28 06:32:46

Tekton Kubernetes Clone

2020-12-01 07:01:41

CSS工具重構

2022-02-21 11:24:14

代碼工具開發

2018-09-05 14:45:10

Python自動化機器學習

2017-12-17 21:58:18

2012-02-09 13:31:03

HibernateJava

2024-11-21 15:24:49

2009-12-15 17:43:04

Ruby自動化驅動

2022-11-15 17:07:40

開發自動化前端

2021-04-19 14:00:03

ExchangelibPython郵箱自動化管理

2024-09-13 15:32:18

2024-01-24 18:50:21

WebFTP服務器

2020-03-10 10:06:08

小程序微信開發

2018-07-13 06:46:35

數據中心自動化微服務

2021-04-17 23:10:59

Python微軟Word

2018-12-03 08:46:36

Web瀏覽器SeleniumPython

2021-09-30 09:00:00

漏洞安全工具

2025-02-06 14:59:08

2018-02-25 19:29:49

自動化數字化IT
點贊
收藏

51CTO技術棧公眾號

永久免费看黄网站| 亚洲精品天堂成人片av在线播放| 97伦伦午夜电影理伦片| 黄色网页在线播放| 日本一不卡视频| 超碰日本道色综合久久综合| 亚洲欧美高清在线| 青青在线视频| 国产成人亚洲综合a∨婷婷| 性色av一区二区三区| 又黄又爽又色的视频| cao在线视频| 国产精品成人午夜| 久久av一区二区| 国产毛片一区二区三区va在线| 偷偷www综合久久久久久久| 欧美日韩精品一区二区天天拍小说| 日本一区二区三区免费观看| 亚洲AV无码精品色毛片浪潮| 日韩一区欧美二区| 久久久久久久久久国产精品| 亚洲欧美va天堂人熟伦| 欧美美女啪啪| 欧美一级片在线| 日本成人在线免费视频| 欧美高清另类hdvideosexjaⅴ| 国产成人精品免费| 国产精品免费一区豆花| 久久日免费视频| 天天躁日日躁狠狠躁欧美| 色婷婷精品大视频在线蜜桃视频| 日韩精品电影网站| 欧美一级一区二区三区| 国产专区综合网| 日韩美女在线观看| av大片在线免费观看| 亚洲性人人天天夜夜摸| 美女撒尿一区二区三区| 91ts人妖另类精品系列| 国产99久久久国产精品成人免费 | 欧美性bbwbbwbbwhd| 狠狠躁日日躁夜夜躁av| 国产美女精品在线| 97视频在线观看视频免费视频| 国产小视频自拍| 欧美天堂社区| 日韩av综合网站| 日本在线不卡一区二区| av成人资源| 精品国产伦一区二区三区观看方式| 国产黄色一级网站| 日韩在线免费电影| 国产精品久久久久久久第一福利| 国产精品加勒比| 亚洲av无码国产精品永久一区 | 久操视频在线免费观看| 国产日韩一区| 久久激情五月丁香伊人| 日本黄色动态图| 国产乱论精品| 日韩av综合网| 亚洲一区视频在线播放| 成人91在线| 久久天天躁日日躁| 麻豆国产尤物av尤物在线观看| 国产成人精品三级高清久久91| 日韩欧美国产综合一区 | 国产精品成人国产| 欧美色倩网站大全免费| 久久成人免费观看| 国产福利在线播放麻豆| 欧美极品美女视频| 天堂av一区二区| 在线观看xxx| 国产成人在线视频网站| 国产乱码精品一区二区三区不卡| 国产又色又爽又黄又免费| 狠狠v欧美v日韩v亚洲ⅴ| 亚洲在线观看视频| 97免费观看视频| 国产精品一区在线| 成人黄色大片在线免费观看| 国产乱叫456在线| 日韩va亚洲va欧美va久久| 国产精品中文在线| www.亚洲天堂.com| 91免费版在线看| 亚洲国产一区二区三区在线播| 水中色av综合| 国产精品午夜免费| 男女裸体影院高潮| 国产人成网在线播放va免费| 亚洲成av人片在www色猫咪| 激情六月丁香婷婷| 4438五月综合| 日韩电影第一页| 久久成人小视频| 日韩午夜免费| 91精品久久久久久久久久| 黄色一级a毛片| 粉嫩aⅴ一区二区三区四区 | 精品国产xxx| 国产曰批免费观看久久久| 久久66热这里只有精品| 免费**毛片在线| 天涯成人国产亚洲精品一区av| 国产精品专区在线| 里番在线播放| 午夜精品一区在线观看| 污色网站在线观看| 精品国产一区二区三区成人影院| 亚洲国产欧美自拍| 久久精品一区二区三区四区五区| 小小影院久久| 国产va免费精品高清在线观看| 日本一本在线观看| 丁香激情综合五月| 91制片厂免费观看| 韩国成人动漫| 亚洲黄色在线看| 欧美成人国产精品一区二区| 伊人成人在线| 99精品国产一区二区| 天天摸天天碰天天爽天天弄| 国产精品国产三级国产普通话三级 | 蜜桃传媒一区二区三区| 久九九久频精品短视频| 精品99久久久久久| 国产一二三四区| 国产女优一区| 国精产品99永久一区一区| 羞羞视频在线免费国产| 在线综合视频播放| 刘亦菲国产毛片bd| 久久在线精品| 欧美三级网色| 主播国产精品| 这里是久久伊人| 黄视频网站免费看| 国产综合久久久久久鬼色| 亚洲午夜精品一区二区| 欧美美女福利视频| 北条麻妃久久精品| 国产精品久久久午夜夜伦鲁鲁| 国产福利91精品| 中文字幕一区二区三区四区五区人| av剧情在线观看| 欧美精品一区二区三区蜜臀| www成人啪啪18软件| 日韩福利电影在线观看| 亚州欧美一区三区三区在线| 蜜臀av在线播放| 日韩女优电影在线观看| 精品人妻在线播放| 成人网男人的天堂| 免费观看美女裸体网站| 日韩mv欧美mv国产网站| 欧美综合在线观看| 爱久久·www| 欧美美女直播网站| 午夜激情福利网| 成人午夜短视频| 91传媒久久久| 欧美呦呦网站| 91中文字幕一区| 九色91在线| 亚洲欧美中文另类| 在线播放成人av| 亚洲激情中文1区| 日本三级日本三级日本三级极| 日韩在线欧美| 91嫩草视频在线观看| av电影院在线看| 亚洲欧美精品在线| 日本一级淫片色费放| 26uuu精品一区二区| 97视频在线免费| 欧美午夜在线播放| 国产一区二区三区视频| 一级视频在线播放| 亚洲综合久久久| 成人免费看aa片| 国产自产高清不卡| heyzo国产| 久久精品av| 国内不卡一区二区三区| 成人午夜一级| 久久久久久久成人| yjizz视频网站在线播放| 日韩视频一区二区三区在线播放| 天堂а√在线中文在线鲁大师| 日韩成人av影视| 人人妻人人澡人人爽欧美一区 | 久久最新视频| 在线观看日韩羞羞视频| 牛牛精品成人免费视频| 欧美一区第一页| 麻豆网站在线| 亚洲欧美中文字幕在线一区| 国产无遮挡又黄又爽又色视频| 久久久影视传媒| 午夜福利123| 久久久久久网| 国产玉足脚交久久欧美| 欧美精品中文| 奇米四色中文综合久久| 国产露出视频在线观看| 日韩三级视频在线看| 午夜一级黄色片| 午夜精彩视频在线观看不卡| 日韩欧美综合视频| 国产日产欧美精品一区二区三区| 污片在线免费看| 亚洲在线免费| 日本黄色片一级片| 亚洲盗摄视频| 成人激情直播| 97久久中文字幕| 国产精品欧美日韩久久| 依依综合在线| 国产91精品视频在线观看| 日韩123区| 久久国产一区二区三区| av中文在线| 亚洲欧洲国产伦综合| 手机亚洲第一页| 欧美日韩国产不卡| 91porny九色| 色综合久久中文综合久久牛| 日韩 欧美 综合| 亚洲高清在线视频| 久久久久成人片免费观看蜜芽| 91免费看视频| 日韩av手机在线播放| 成人爽a毛片一区二区免费| 能看毛片的网站| 麻豆精品一区二区综合av| www.色就是色| 日韩一区欧美二区| 男女啪啪网站视频| 日韩经典一区二区| 三级a在线观看| 蜜芽一区二区三区| www.se五月| 久久99精品久久久久婷婷| 老太脱裤子让老头玩xxxxx| 亚洲精品乱码| 9久久9毛片又大又硬又粗| 伊人狠狠色j香婷婷综合| 国产一二三在线视频| 在线观看日韩av电影| 成人免费aaa| 久久免费国产| 国产又大又黄又猛| 久久99国产乱子伦精品免费| 亚洲精品综合在线观看| 国内精品免费在线观看| 91n.com在线观看| 欧美aaaaa成人免费观看视频| 国产极品尤物在线| 亚洲欧美日韩精品一区二区| 虎白女粉嫩尤物福利视频| 久久久久久一区二区| 国产精品区在线| 老牛影视一区二区三区| 日韩一级性生活片| 在线视频日韩| 男女无套免费视频网站动漫| 久久99精品久久久久久| 自拍偷拍 国产| 国产一区二区美女| 精品人妻一区二区免费| 久久免费精品国产久精品久久久久| 荫蒂被男人添免费视频| 久久久99免费| 日本精品在线观看视频| 亚洲人妖av一区二区| 日本三级视频在线| 91福利视频在线| 国产av一区二区三区| 日韩精品免费在线播放| 91se在线| 久久久久久久久久婷婷| 日本小视频在线免费观看| 国产91精品不卡视频| 成人av三级| 91亚洲va在线va天堂va国| 日韩久久一区| 国内精品视频免费| 亚洲成a人片77777在线播放| 亚洲人久久久| 亚洲毛片一区| www.色就是色.com| 久久最新网址| 色妞www精品视频| 波多野结衣视频免费观看| 色婷婷久久久| 精品动漫一区| 成人午夜免费剧场| 国产精品普通话对白| 日韩精品aaa| 91浏览器在线视频| 欧美做爰爽爽爽爽爽爽| 欧美午夜女人视频在线| 精品国产乱码久久久久久鸭王1| 中文字幕在线观看不卡| 国产极品美女高潮无套嗷嗷叫酒店| 亚洲综合色视频| 中文字幕在线播出| 亚洲精品99久久久久| 国产成人l区| 日韩男女性生活视频| 成人性生交大片免费看96| 91社在线播放| 奇米色一区二区三区四区| 在线黄色免费网站| 亚洲免费电影在线| 中文字幕+乱码+中文字幕明步| 欧美日韩成人综合在线一区二区| 91资源在线视频| 欧美tk丨vk视频| 在线观看h片| 欧洲美女免费图片一区| 国产成人福利av| 免费观看中文字幕| 日本91福利区| 善良的小姨在线| 国产精品女同一区二区三区| 国产精品va无码一区二区三区| 日本电影一区二区| 成年丰满熟妇午夜免费视频| 久久精品国产亚洲高清剧情介绍| 天天影视色综合| 国产二区国产一区在线观看| 99久久99久久精品免费看小说.| 亚洲欧美一区二区三区孕妇| 中文字幕日韩经典| 亚洲午夜精品久久久久久性色| 蜜桃视频网站在线| 国产精品免费久久久久久| 第四色在线一区二区| 一本色道久久88亚洲精品综合| 亚洲免费网站| 成年人的黄色片| 性欧美大战久久久久久久久| 伊人久久亚洲综合| 最新国产成人av网站网址麻豆| 成人在线免费观看黄色| 国产精品中文字幕在线| 欧美hd在线| av在线免费看片| 亚洲精品第1页| 中文字幕视频二区| 日韩中文字幕视频| 国产精品99久久免费| 青青影院一区二区三区四区| 国产 欧美 精品| 欧美麻豆久久久久久中文 | 国产成人a视频高清在线观看| 国产自产在线视频一区| 国产亚洲精品v| 国产在线不卡av| 色综合天天综合网天天看片| 九九热视频在线观看| 国产综合福利在线| 国产精品久久| 中文字字幕码一二三区| 欧洲一区二区三区免费视频| 麻豆av在线导航| 国产精品久久久久免费| 久久久久久黄| 久久中文字幕人妻| 91极品美女在线| 美女免费久久| 国产三级精品在线不卡| 中文在线播放一区二区 | 9999在线视频| 成人午夜影院在线观看| 在线视频精品| 国产精品麻豆一区| 亚洲成色999久久网站| 日本少妇一区| 欧美一区二区三区综合| 久久久综合视频| 精品国产黄色片| 国产ts一区二区| 欧美91大片| 亚洲天堂久久新| 色综合久久久久综合99| 蜜桃视频网站在线| 久久精品人人做人人爽电影| 久久国产精品免费| 国产午夜福利一区二区| 一区国产精品视频| heyzo欧美激情| 青青草久久伊人| 欧美日韩中文字幕| 成人短视频在线| 日本一区二区三区视频免费看| 日韩专区中文字幕一区二区|