精品欧美一区二区三区在线观看 _久久久久国色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技術棧公眾號

√天堂资源在线| 中文字幕在线中文字幕日亚韩一区| 国产精品7777777| 夜色77av精品影院| 欧美日韩国产成人在线91| 看一级黄色录像| 日韩午夜影院| 国产一区中文字幕| 2025国产精品视频| 男人晚上看的视频| 久久夜色电影| 欧美久久高跟鞋激| 69堂免费视频| 中文在线观看免费| 国产欧美视频在线观看| 国产91一区二区三区| 男人的天堂av网站| 激情欧美日韩一区| 日韩在线观看免费全| 日本黄色免费观看| 精品国产乱码久久久久久樱花| 精品久久久视频| 一区二区三区四区免费观看| 韩国精品视频| 成人av中文字幕| 亚洲xxxx3d| 在线观看国产黄| 日韩一级在线| 欧美第一淫aaasss性| 色婷婷国产精品免| 综合综合综合综合综合网| 日韩欧美国产精品| 日本中文字幕影院| 666av成人影院在线观看| 亚洲一区二区黄色| 亚洲小说欧美另类激情| 1pondo在线播放免费| 久久婷婷国产综合国色天香 | 国产精品字幕| 精品国产福利在线| 国产一二三区在线播放| 麻豆传媒在线观看| 国产精品视频看| 色就是色欧美| 国产精品一二三区视频| 91麻豆精品在线观看| 国产综合18久久久久久| 国产成人手机在线| 国产成人aaa| 999国内精品视频在线| 一二三区在线播放| 蜜桃精品视频在线观看| 国产精品视频网址| 中文在线观看免费高清| 久久亚洲精品伦理| 欧美一区二区三区免费视| 日本一区二区三区免费视频| 激情欧美国产欧美| 91国产高清在线| 成人毛片18女人毛片| 一区二区国产精品| 日韩免费av片在线观看| 樱花视频在线免费观看| 日本色综合中文字幕| 国产精品入口日韩视频大尺度| 最近中文字幕免费在线观看| 久久精品国产一区二区三| 成人免费大片黄在线播放| 国产精品久久久久久免费免熟 | 国产精品自产拍高潮在线观看| 中文字幕免费高清网站| 麻豆成人久久精品二区三区红| 国产欧美一区二区三区在线| 国产成a人亚洲精v品无码| 国产成人高清在线| 欧美激情论坛| 日本亚洲精品| 亚洲午夜免费福利视频| www.com毛片| 国产韩日精品| 91精品国产高清一区二区三区| 中文字幕在线观看91| 一区二区三区视频免费观看| 最新日韩中文字幕| 麻豆成人在线视频| 欧美一级专区| 亚洲影院色无极综合| 少妇无码一区二区三区| 国产亚洲一区字幕| 麻豆映画在线观看| 正在播放日韩精品| 欧美日韩dvd在线观看| 精品久久久久一区二区| 成人羞羞网站| 欧美交受高潮1| 波多野结衣理论片| 国产91富婆露脸刺激对白| 日本视频一区二区在线观看| 超碰porn在线| 色综合天天做天天爱| 色91精品久久久久久久久| 久久99精品久久久久久欧洲站| 在线观看亚洲区| 国产性生活网站| 男女男精品视频| 国产精品10p综合二区| 成年人视频在线免费观看| 亚洲视频一区在线| 日本男人操女人| 99ri日韩精品视频| 亚洲性xxxx| 国产无遮挡又黄又爽又色| 免费在线观看成人| 久久精品日韩精品| av在线免费网址| 欧美三级蜜桃2在线观看| 久久久久久久无码| 欧美日韩精品一本二本三本| 国产精品免费在线免费| 天堂中文在线资源| 一区二区高清在线| 伊人色在线观看| 久久99影视| 隔壁老王国产在线精品| 国产又粗又猛又黄又爽| 欧美国产日韩a欧美在线观看| 分分操这里只有精品| 日韩一区二区三区精品| 中文字幕自拍vr一区二区三区| 亚洲成熟少妇视频在线观看| 成人激情小说乱人伦| 91精品国产吴梦梦| 亚洲精品大全| 俺去亚洲欧洲欧美日韩| 亚洲精品毛片一区二区三区| 久久久久久电影| 国产a级一级片| 日韩精品丝袜美腿| 国内精品视频一区| 午夜精品久久久久久久爽| 亚洲美女免费视频| 五月天丁香花婷婷| 91精品国产调教在线观看| 国产一区欧美二区三区| 亚洲精品承认| 欧美日韩久久一区| 国产又粗又长又硬| 久久 天天综合| 色撸撸在线观看| 国产在线视频欧美一区| 九九热精品在线| www黄色网址| 亚洲一级二级在线| 亚洲欧美日韩偷拍| 国产偷自视频区视频一区二区| 国内视频一区| 在线观看欧美日韩电影| 亚洲日本中文字幕免费在线不卡| caoporn国产| 国产女人18毛片水真多成人如厕 | 精品美女一区| 社区色欧美激情 | 国产片高清在线观看| 一区二区三区在线观看视频| 国产精品欧美性爱| 妖精视频成人观看www| 久久久99国产精品免费| 久久青青视频| 日韩在线观看免费av| 精品久久在线观看| 午夜久久电影网| 成熟人妻av无码专区| 久久精品国产77777蜜臀| 三年中国中文在线观看免费播放| 97久久亚洲| 日本亚洲欧洲色α| 免费高清完整在线观看| 亚洲国模精品一区| 一区二区乱子伦在线播放| 亚洲人精品一区| 亚洲最大免费视频| 美女网站一区二区| 五月丁香综合缴情六月小说| 免费av一区二区三区四区| 国产日韩中文在线| 91福利区在线观看| 国产一区二区三区在线观看视频| 国产又黄又爽视频| 欧美午夜无遮挡| 性色av无码久久一区二区三区| 99视频热这里只有精品免费| 日韩不卡一二三| 亚洲青涩在线| 精品国产无码在线| 日韩在线你懂的| 91亚洲永久免费精品| 涩涩视频在线| 欧美精品少妇videofree| 九色在线观看| 精品三级在线看| 中文字幕一区2区3区| 亚洲成av人影院| 尤物在线免费视频| 91浏览器在线视频| 亚洲成人av免费观看| 久久狠狠婷婷| www.成年人视频| 国产精品久久久久久久久妇女| 精品亚洲第一| 欧美国产中文高清| 国产精品免费久久久久影院| 嗯啊主人调教在线播放视频| 久久久999精品| 国产一级网站视频在线| 欧美精品一区二区三区久久久| 一级aaaa毛片| 欧美在线不卡视频| 国产精品100| 亚洲图片自拍偷拍| 欧美国产日韩在线观看成人| 中文字幕成人网| 亚洲色成人网站www永久四虎| 99视频超级精品| 久久久久国产免费| 国产一区二区三区久久久| 亚洲一区在线不卡| 久久www成人_看片免费不卡| 日韩极品视频在线观看| 欧美久久一区| 99re6这里有精品热视频| 久久视频精品| 亚洲高清在线观看一区| 国产成人久久| 欧美不卡三区| 亚洲国产合集| 欧美aaaaa喷水| 亚州综合一区| 欧美精品二区三区四区免费看视频 | 欧美在线二区| 天天在线免费视频| 91成人精品| 特大黑人娇小亚洲女mp4| 亚洲国产精品久久久天堂| 伊人色综合影院| 99精品在线观看| 精品嫩模一区二区三区| 欧美在线高清| 91黄色在线看| av不卡在线| 黄在线观看网站| 久色成人在线| 亚洲一级片网站| 国产一区二区在线影院| 好吊操视频这里只有精品| 懂色av一区二区在线播放| 国内精品免费视频| 97精品久久久午夜一区二区三区| www.超碰97| 欧美韩国日本综合| 激情无码人妻又粗又大| 亚洲免费观看高清在线观看| 精品欧美一区二区久久久久| 亚洲最色的网站| 国产免费观看av| 在线精品视频一区二区三四 | 蜜臀va亚洲va欧美va天堂| 欧美女同在线观看| 国产一区二区导航在线播放| 免费观看污网站| 26uuu国产在线精品一区二区| 摸摸摸bbb毛毛毛片| 中文字幕日韩一区| 久久午夜无码鲁丝片| 一本色道a无线码一区v| 一区二区日韩在线观看| 日韩欧美一级精品久久| 午夜18视频在线观看| 在线观看国产成人av片| 4438x成人网全国最大| 韩国日本不卡在线| 欧美va视频| 97人人模人人爽人人少妇| 亚洲精品白浆高清| 中文网丁香综合网| 国产精品日韩| 五月天激情播播| 97精品超碰一区二区三区| wwwww黄色| 亚洲午夜激情av| 亚洲视屏在线观看| 精品国产麻豆免费人成网站| 国产免费av高清在线| 九色精品美女在线| 自拍视频在线看| 亚洲精品女av网站| 久久av中文| 久久久久久久香蕉| 日韩综合在线视频| 9.1在线观看免费| 国产精品成人一区二区艾草| 日本高清www免费视频| 91麻豆精品国产91久久久资源速度| 天堂网在线中文| 超碰91人人草人人干| 免费观看亚洲| 粉嫩av一区二区三区免费观看| 国产一区二区三区日韩精品| 蜜臀精品一区二区| 国内久久婷婷综合| 老熟妇一区二区| 欧美日韩国产中字| 亚洲精品一区二区三区四区| 日韩一级黄色av| 日韩欧美精品一区二区综合视频| 国产欧美一区二区在线播放| 伊人久久大香线蕉精品组织观看| 情侣黄网站免费看| 91影院在线观看| 精品一区二区三区人妻| 制服丝袜日韩国产| 天堂资源在线中文| 国产精品老女人精品视频| 日韩有码av| 丰满爆乳一区二区三区| 丁香婷婷综合网| 精品少妇一二三区| 欧美成人a在线| √天堂8在线网| 亚洲精品欧美极品| 综合五月婷婷| 久久综合在线观看| 中文字幕亚洲综合久久菠萝蜜| 亚洲 国产 日韩 欧美| 国产一区二区三区丝袜 | 成人精品网站在线观看| 日韩精品影视| 欧美三级理论片| 中文av字幕一区| 亚洲天堂avav| 日韩在线观看网站| 亚洲欧洲日韩精品在线| 欧美 另类 交| 国产一区二区三区在线观看免费 | 最近中文字幕在线mv视频在线| 欧美日韩国产精品专区 | 欧美成人激情视频| 国产精品毛片aⅴ一区二区三区| 波多野结衣三级在线| 国产一区二区免费在线| 在线免费日韩av| 精品久久久久一区| 俺来俺也去www色在线观看| 国产精品一区二区不卡视频| 亚洲国产专区校园欧美| 99久久人妻精品免费二区| 日韩人体视频一二区| 久久av少妇| 国产精品自产拍在线观| 午夜精品视频一区二区三区在线看| 99999精品| 亚洲高清中文字幕| 性感美女福利视频| 日本午夜在线亚洲.国产| 欧美自拍偷拍| 黄色小视频免费网站| 一区二区三区四区五区视频在线观看| 亚洲av无码乱码在线观看性色| 国内精久久久久久久久久人| 亚洲成aⅴ人片久久青草影院| 国产一级不卡毛片| 亚洲品质自拍视频网站| 色呦呦中文字幕| 国产精品久久久久久久av大片 | 美女国内精品自产拍在线播放| 亚洲一区二区三区免费| 免费 成 人 黄 色| 欧美激情一区二区三区蜜桃视频| 99在线精品视频免费观看20| 欧美亚洲视频在线观看| 日韩精品不卡一区二区| 黑森林av导航| 欧美亚洲一区二区在线| 在线看三级电影| 欧美日韩国产综合视频在线| 激情图片小说一区| 亚洲一区欧美在线| 中文欧美日本在线资源| 91精品国产自产在线丝袜啪| 无码人妻h动漫| 亚洲靠逼com| 美女欧美视频在线观看免费 | www国产一区| 国产精品国产三级国产aⅴ浪潮| 午夜性色一区二区三区免费视频| 女~淫辱の触手3d动漫| 欧美一区二区视频在线观看2020 | 1区2区3区在线视频| 欧美日韩精品免费看| 国产成人精品影视| 在线视频精品免费|