Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
ts-morph と ast-grep でたくさんの TypeScript コードを書き換えた話
Search
Tomoya Chiba
January 12, 2024
Technology
4
3.5k
ts-morph と ast-grep でたくさんの TypeScript コードを書き換えた話
Tokyoto.js #02 - Kyoto.js in Tokyo (
https://kyotojs.connpass.com/event/302442/
) で発表した資料です。
Tomoya Chiba
January 12, 2024
Tweet
Share
More Decks by Tomoya Chiba
See All by Tomoya Chiba
東京Ruby会議12ヘルパー楽しかった✌
tomoasleep
0
42
rbs-inline 生成してみた
tomoasleep
1
240
LiveShare で森羅万象を共同編集する(?)
tomoasleep
1
460
GitHub Actions による RSpec の時間を半分以上短縮した話
tomoasleep
2
1.1k
render 出来るオブジェクトの作り方
tomoasleep
0
190
Rails アプリを10年以上継続していくためのフロントエンドの底上げ
tomoasleep
3
870
Rails のブラウザテストを Playwright で動かすようにしたらデバッグが簡単になって捗った
tomoasleep
3
2.8k
Sorbetやっていき(たい)宣言
tomoasleep
0
390
RubyKaigi に貢献したくなる組織の作り方
tomoasleep
1
770
Other Decks in Technology
See All in Technology
Culture Deck
optfit
0
330
依存関係があるコンポーネントは Barrel ファイルでまとめよう
azukiazusa1
3
530
Moved to https://speakerdeck.com/toshihue/presales-engineer-career-bridging-tech-biz-ja
toshihue
2
550
データ基盤の成長を加速させる:アイスタイルにおける挑戦と教訓
tsuda7
3
650
自動テストの世界に、この5年間で起きたこと
autifyhq
10
7.1k
現場の種を事業の芽にする - エンジニア主導のイノベーションを事業戦略に装着する方法 -
kzkmaeda
2
1.5k
10分で紹介するAmazon Bedrock利用時のセキュリティ対策 / 10-minutes introduction to security measures when using Amazon Bedrock
hideakiaoyagi
0
170
『AWS Distinguished Engineerに学ぶ リトライの技術』 #ARC403/Marc Brooker on Try again: The tools and techniques behind resilient systems
quiver
0
130
エンジニアの育成を支える爆速フィードバック文化
sansantech
PRO
3
670
個人開発から公式機能へ: PlaywrightとRailsをつなげた3年の軌跡
yusukeiwaki
11
2.7k
5分で紹介する生成AIエージェントとAmazon Bedrock Agents / 5-minutes introduction to generative AI agents and Amazon Bedrock Agents
hideakiaoyagi
0
220
Postmanを使いこなす!2025年ぜひとも押さえておきたいPostmanの10の機能
nagix
2
120
Featured
See All Featured
Practical Orchestrator
shlominoach
186
10k
The MySQL Ecosystem @ GitHub 2015
samlambert
250
12k
Mobile First: as difficult as doing things right
swwweet
223
9.3k
Testing 201, or: Great Expectations
jmmastey
41
7.2k
Documentation Writing (for coders)
carmenintech
67
4.6k
Intergalactic Javascript Robots from Outer Space
tanoku
270
27k
The Cost Of JavaScript in 2023
addyosmani
47
7.3k
Why Our Code Smells
bkeepers
PRO
335
57k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
356
29k
GraphQLの誤解/rethinking-graphql
sonatard
68
10k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
129
19k
YesSQL, Process and Tooling at Scale
rocio
171
14k
Transcript
Tomoya Chiba (@tomoasleep) ts-morph と ast-grep でたくさんの TypeScript コ ードを書き変えた話
1
千葉 知也 (@tomoasleep) Qiita 株式会社 エンジニア Tokyo と Nagoya に拠点があります
自分は Tokyo の民です Ruby と TypeScript と HCL と YAML を書いたり消した りする仕事をしています 自己紹介 2
ライブラリ移行 → たくさん書き換えないと行けない → つらい!!無理!! ts-morph と ast-grep を使って ↑
に立ち向かう話 今日のお題 3
移行したもの: GraphQL のコード生成ライブラリ apollo-tooling (Deprecated) → GraphQL Code Generator type
User implements Node { id: ID! username: String! email: String! } query FindUser($id: ID!) { user(id: $id) { id username email } } 今回移行したライブラリ 4
移行したもの: GraphQL のコード生成ライブラリ apollo-tooling (Deprecated) → GraphQL Code Generator //
こんな感じのコードを生成してくれる export interface User = Node & { __typename?: 'User'; id: ID; username: string; email: string; }; export interface GetUser { user: GetUser_user; } export interface GetUser_user { id: string; username: string; email: string; 今回移行したライブラリ 5
移行したもの: GraphQL のコード生成ライブラリ apollo-tooling (Deprecated) → GraphQL Code Generator 今回移行したライブラリ
6
生成されるものがめちゃくちゃ変わってしまう ( ファイル名, export, ...etc) これをほとんどのコードに対して行う必要がある import GetItemQuery from '~/graphql/generated/someQueryByQueryName.gql'
import { GetItem, GetItemVariable, GetItem_items } from '~/graphql/generated/types/someQueryB const { data, refetch } = useQuery<GetItem, GetItemVariables>(GetItemQuery) const items: GetItem_items[] = data.items ↓ import { GetItem, GetItemDataDocument } from '~/graphql/generated/some-query-by-file-name' type GetItem_items = GetItem['items'][number] const { data, refetch } = useQuery(GetItemDocument) const items: GetItem_items[] = data.items 色々書き換える必要が出てくる 7
(VSCode の機能とか頑張って使えばなんとかなるんじゃね?) 結論: 失敗 人がやるとミスが出る 最初の試行錯誤した部分と、その後で書き換え方がブレて一貫性がない 後から発生する要望、フィードバックに答えられない Conflict への対応が重すぎ レビュアーの負担も重い
Try 1: 手で頑張って置き換えてみる 8
ts-morph, ast-grep 等のツールを使う 全ての書き換えをスクリプト化する 手での書き換えは ( 基本) やらない # 書き換えに使ったスクリプト例
( 後のスライドで再掲します) # コードの状態をリセット BASE_COMMIT_ID=$(git log --grep="XXXXXXX" --author="tomo.asleep" -n 1 --pretty=%H) git restore --source=$BASE_COMMIT_ID --worktree -- src/ # コードを書き換え ast-grep --pattern 'useQuery<$$$>($$$P)' --rewrite 'useQuery($$$P)' \ --lang ts --update-all src/ ts-node ./rewriter-with-ts-morph.ts # Linter, Formatter でコードを整形 yarn run format Try 2: 自動化する 9
https://ts-morph.com/ TypeScript ファイルの書き換えを行える AST 操作だけでなく、様々な書き換え機能を提供 VSCode のリファクタリング機能と出来ることは大体行える ( と思う) 公式ドキュメントが分かりやすい
ts-morph 10
import { Project } from "ts-morph"; const project = new
Project(); const myCode = project.getSourceFile("myCode.ts"); // import MyClass from "./file"; を追加 myCode.addImportDeclaration({ defaultImport: "MyClass", moduleSpecifier: "./file", }): // type TypeAlias = string; を追加 myCode.addTypeAlias({ name: "TypeAlias", type: "string", }); await myCode.save(); ts-morph でコードの追加が簡単に行える 11
定義、import した変数とその利用箇所をまとめて rename 出来る // ts-morph で import を書き換える const
importDeclaration = myCode.getImportDeclaration("package") const namedImports = importDeclaration.getNamedImports() namedImports.forEach(namedImport => { // import 名と、利用箇所をまとめて rename する namedImport.setName("NewName") }) /* ----------------------------------------------------------------------------- */ // Before import { OldName } from "package" someFunction(OldName) // ↓ // After import { NewName } from "package" someFunction(NewName) ts-morph で複雑な書き換えが簡単に行える 12
使ってない import の削除も簡単に出来る sourceFile.fixUnusedIdentifiers(); /* ----------------------------------------------------------------------------- */ // Before import
{ OldName, NewName } from "package" someFunction(NewName) // ↓ // After import { NewName } from "package" someFunction(NewName) ts-morph で複雑な書き換えが簡単に行える 13
https://ast-grep.github.io/ AST を加味した grep, sed, awk みたいなもの 様々な言語 (TypeScript, Ruby,
Python, Go, Rust, ...etc) に対応 直感的に書きやすい && 何をやっているかわかりやすい シンプルな書き換えはこちらを使った ast-grep --pattern 'useQuery<$, $>($$$A)' --rewrite 'useQuery($$$A)' \ --lang ts --update-all src/ // Before const data = useQuery<FetchData, FetchDataVariables>(FetchDataQuery) // ↓ // After const data = useQuery(FetchDataQuery) ast-grep 14
コードをリセット → 書き換え → Linter 等で整形、を順に行うスクリプトを書く 冪等になるようにスクリプト化しておくと便利 書き換えの試行錯誤がしやすくなる Conflict 解消、変更への追従が簡単
# コードの状態をリセット BASE_COMMIT_ID=$(git log --grep="XXXXXXX" --author="tomo.asleep" -n 1 --pretty=%H) git restore --source=$BASE_COMMIT_ID --worktree -- src/ # コードを書き換え ast-grep --pattern 'useQuery<$$$>($$$P)' --rewrite 'useQuery($$$P)' \ --lang ts --update-all src/ ts-node ./rewriter-with-ts-morph.ts # Linter, Formatter でコードを整形 yarn run format 書き換えをスクリプト化した 15
書き換えスクリプトは1 日で作れた (※ 個人差があります) ts-morph, ast-grep 公式ドキュメントが充実してて、調べながらやりやすい スクリプト化しておくと色々良かった レビューでの説明がある程度楽 レビューでの指摘を柔軟に取り入れられる
Conflict 解消、変更への追従が簡単で、他の人の作業をブロックしにくい レビュアーに睨まれたりしない やってみてのまとめ 16