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
Generate React Component with TypeScript AST
Search
Yosuke Kurami
November 18, 2021
Programming
3.4k
6
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Generate React Component with TypeScript AST
Yosuke Kurami
November 18, 2021
More Decks by Yosuke Kurami
See All by Yosuke Kurami
TypeScript LSP の今までとこれから
quramy
1
2.1k
フロントエンドテストの育て方
quramy
12
3.9k
App Router 悲喜交々
quramy
8
750
上手に付き合うコンポーネントテスト
quramy
6
2.4k
Patched fetch did not work
quramy
6
800
GraphQL あるいは React における自律的なデータ取得について
quramy
18
5.9k
Next.js App Router
quramy
15
3.9k
Fragment Composition of GraphQL
quramy
17
4.9k
reg-viz VRT tools
quramy
4
1.7k
Other Decks in Programming
See All in Programming
PostgreSQL 18で考えるUUID主キー
kazuhiro1982
0
470
複数の Claude Code が"放置"されてしまう問題をCLI ダッシュボードを自作して解決した話
sumihiro3
1
680
Android CLI
fornewid
0
220
VibeCodingからAgenticWorkflowへ
starfish719
0
400
Google Apps Script で Ruby を動かす
kawahara
0
150
テーブルをDELETEした
yuzneri
0
140
Laravelで学ぶ Webアプリケーションチューニング入門/web_application_tuning_101
hanhan1978
4
1.7k
php-fpmのプロセスが枯渇した日-調査・対処・そして本当にやるべきだったこと-
shibuchaaaan
0
270
the container ship “Apple Silicon”@WWDC26 Recap -Japan-\(region).swift
shingangan
0
120
進化を続けるGo toolsの現在地 / The Current State of Ever-Evolving Go Tools
hond0413
0
200
20260722_microCMSで考える、AI時代のコンテンツ運用設計
yosh1
0
390
Go 1.27 における memory allocation の高速化
andpad
0
160
Featured
See All Featured
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
141
35k
Rebuilding a faster, lazier Slack
samanthasiow
85
9.6k
WENDY [Excerpt]
tessaabrams
11
39k
Scaling GitHub
holman
464
140k
Pawsitive SEO: Lessons from My Dog (and Many Mistakes) on Thriving as a Consultant in the Age of AI
davidcarrasco
0
210
The browser strikes back
jonoalderson
0
1.5k
Six Lessons from altMBA
skipperchong
29
4.4k
Git: the NoSQL Database
bkeepers
PRO
432
67k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
38
2.9k
Docker and Python
trallard
47
4.1k
The Spectacular Lies of Maps
axbom
PRO
1
900
Unlocking the hidden potential of vector embeddings in international SEO
frankvandijk
0
890
Transcript
Generate React Component with TypeScript AST 2021.11.18
About me - Twitter/GitHub: @Quramy - ϦΫϧʔτͰWebϑϩϯτΤϯδχΞͬͯ·͢ - ࠓͷ7݄ࠒ͔ΒελσΟαϓϦͷࣄΛ͢ΔΑ͏ ʹͳΓ·ͨ͠
ݸਓతͳ OSS works - storycap reg-suit: StorybookΛ༻͍ͨVisual Regression Testing πʔϧ܈
- tsuquyomi: TypeScript Language Server ͷ Vim client - ts-graphql-plugin: TypeScript Client ͚ͷGraphQL ։ൃ༷πʔϧ܈ - typed-css-modules: CSS Modules Λ TypeScriptͰѻ͏ͨΊͷϢʔςΟϦςΟ
ݸਓతͳ OSS works - storycap reg-suit: StorybookΛ༻͍ͨVisual Regression Testing πʔϧ܈
- tsuquyomi: TypeScript Language Server ͷ Vim client - ts-graphql-plugin: TypeScript Client ͚ͷGraphQL ։ൃ༷πʔϧ܈ - typed-css-modules: CSS Modules Λ TypeScriptͰѻ͏ͨΊͷϢʔςΟϦςΟ ࣝɾڵຯ͕UTʹภ͍ͬͯΔ
ࠓ͢͜ͱ - ࠓࠓͱͯ TypeScript ͷ - QuramyࢀըதͷελσΟαϓϦϓϩδΣΫτ: Next.js + TypeScript
+ GraphQL ͳ ΞϓϦέʔγϣϯ ※ Web෦ʹݶΔ. ผ్ωΠςΟϒApp͋Δ - ϓϩδΣΫτͰ࣮ࢪ͍ͯ͠Δ Figma -> React Component (TSX) ͷࣗ ಈੜ෦ʹ͍ͭͯհ
Agenda 1. σβΠϯγεςϜͱ Iconography Token 2. Iconography React Component ͷࣗಈੜ
Agenda 1. σβΠϯγεςϜͱ Iconography Token 2. Iconography React Component ͷࣗಈੜ
σβΠϯγεςϜ - ݱࡏࢀը͍ͯ͠ΔϓϩδΣΫτͷσβΠϯγεςϜͷಛ - Designer ͕ Figma Ͱཧ ※ Figma
ʹରԠ͢Δ࣮ʹ͍ͭͯ Web Devs / Native app Devs ͷ - ίϯϙʔωϯτࢤ - Atomic Design ϕʔε
“Extended” Atomic Design https://bradfrost.com/blog/post/extending-atomic-design/
Design Token ͱ - ͦΕࣗUIཁૉʹͳΓ͑ͳ͍͕ɺͦΕͳͯ͘͠σβΠϯγεςϜΛ࣮ ݱͰ͖ͳ͍ߏͷ͜ͱ - e.g. ৭มλΠϙάϥϑΟͳͲ -
Extended Atomic Design Ͱ Atoms ΑΓͳ͓ԼҐͷଘࡏ - (༨ஊ) ߏจղੳͰʮ͜ΕҎ্ղෆՄೳͳจࣈྻʯͱ͍͏ҙຯͰ token ͱ͍͏ݴ༿͕Ͱͯ͘Δ
Design Tokenͷҙٛ - Token ϓϥοτϑΥʔϜʹґଘ͠ͳ͍ ϓϩμΫτσβΠϯͷࠜݩ - TokenΛϓϥοτϑΥʔϜݻ༗ͷݴޠ (React
Kotlin) ʹ༁ɾมͯ͠ར༻͢Δ͜ͱͰɺ Ұ؏ੑͷ͋ΔσβΠϯͱͳΔ - ࣗಈੜͱ૬ੑ͕ྑ͍ UI Component for PC Web UI Component for Android Token Automatic Compilation
Token Kinds Web ΞϓϦέʔγϣϯʹ͓͚Δ Design Token ࣮ݱखஈ(Ұྫ) छྨ ࣮ܗଶ $PMPS4IBEPX(VUUFS
$44$VTUPN1SPQFSUJFT PS +BWB4DSJQUDPOTUBOU $44JO+4 5ZQPHSBQIZ $44!GPOUGBDF MJOFIFJHIUGPOUTJ[FQSPQT *DPOPHSBQIZ 47(pMFPS*OMJOF47(PS8FC'POUTFUD
Token Kinds Web ΞϓϦέʔγϣϯʹ͓͚Δ Design Token ࣮ݱखஈ(Ұྫ) छྨ ࣮ܗଶ $PMPS4IBEPX(VUUFS
$44$VTUPN1SPQFSUJFT PS +BWB4DSJQUDPOTUBOU $44JO+4 5ZQPHSBQIZ $44!GPOUGBDF MJOFIFJHIUGPOUTJ[FQSPQT *DPOPHSBQIZ 47(pMFPS*OMJOF47(PS8FC'POUTFUD
Iconography Token - ৭มϑΥϯτάϦϑͱಉ༷ʹΞΠίϯΛߏ͢Δύεใ Design Token - ϓϩδΣΫτͰɺWeb Ͱͷ Token࣮ܗଶͱͯ͠
Inline SVGΛબ - Atoms Component͔Β͍͍͢Α͏ʹɺIconography Tokenࣗମ React Componentͱ࣮ͯ͠
Iconography Token via React - ྫ: υϩοϓμϯϦετ - ҙຯΛ࣋ͭ࠷খUI୯Ґ υϩοϓμϯ
▼ ෦ͷ SVG ୯ମʹҙຯແ͍ Iconography Token Dropdown Component (Atoms) import { Down } from "../../tokens/Iconography/Common" export function Dropdown() { return ( <> {/* தུ */} <Down /> </> ) }
Agenda 1. σβΠϯγεςϜͱ Iconography Token 2. Iconography React Component ͷࣗಈੜ
PC Web Devs workflow ϓϩδΣΫτͰͷେ·͔ͳը໘։ൃϫʔΫϑϩʔ 'JHNBͰ6*694QFD֬ೝ 3FBDU54Ͱ࣮
4UPSZCPPLͰ֬ೝςετ
None
ίΠπϥΛΰχϣΰχϣͯ͠ 3FBDU$PNQPOFOUʹͨ͠Ε
import { css } from '@emotion/react' import { assertNever }
from 'assert-never' import { gray } from '../../colors' import { IconSize } from '../../sizing' export type Props = { readonly iconType: 'Outlined' | 'Filled' readonly css?: JSX.IntrinsicAttributes['css'] readonly size: IconSize } export function Home(props: Props) { const { iconType, size } = props const cssObj = (props as any).className ?? css` fill: ${gray[500]}; ` switch (iconType) { case 'Filled': { return ( <svg xmlns='http://www.w3.org/2000/svg' width={size} height={size} viewBox='0,0,24,24' css={cssObj}> <path d='M14 19a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1v-3a2 2 0 0 1 4 0v3Zm9.578-8.797.01-.016-11-8.002-.128-.065-.108-.055a.985.985 0 0 0-.7 fillRule='evenodd' /> </svg> ) } case 'Outlined': { return ( <svg xmlns='http://www.w3.org/2000/svg' width={size} height={size} viewBox='0,0,24,24' css={cssObj}> <path d='M20 19.994h-5v-4a3 3 0 0 0-6 0v4H4V10.05l8-5.818 8 5.818v9.945Zm-9 0v-4a1 1 0 0 1 2 0v4h-2ZM23.578 10.2l.01-.015-11-8-.128-. fillRule='evenodd' /> </svg> ) } default: return assertNever(iconType) } } Iconography Token Component ͷத
Why inline SVG? ଞํࣜͱൺֱ͢ΔͱɺInline SVG ͕ Icon Tokenͷ࣮ʹ࠷ద͍ͯ͠Δ ํ๏ QSPTDPOT
JNH 47(pMF 'JHNB͔ΒҰׅͰ47(ϑΝΠϧΛFYQPSU͢Δ͚ͩ ৭Λมߋ͢Δͷ͕༰қͰͳ͍ %FTJHO5PLFOͱͯ͠க໋త 8FC'POUT ७ਮʹ໘ ςΩετͱͯ͠ղऍ͞ΕΔͨΊɺҙਤͤ͵࠷దԽ͕ͳ͞ΕΔ ಛʹXFCLJUܥ *OMJOF47( 3FBDU$PNQPOFOU 5SFF4IBLBCMF ͱ͍͕͑ແ͍Θ͚Ͱͳ͍˞ޙड़
Figma to TSX (खಈ) - Figma͔ΒSVGίʔυΛίϐϖ - svgo Ͱѹॖ, svg2jsx
Ͱ JSXԽ - ΤσΟλʹషΓ͚ͨޙɺΖΖΛ ͑ͯอଘ
Figma to TSX (ࣗಈ?) - Figma͔ΒSVGίʔυΛίϐϖ - svgo Ͱѹॖ, svg2jsx
Ͱ JSXԽ - ΤσΟλʹషΓ͚ͨޙɺΖΖΛ ͑ͯอଘ 'JHNB"1*Ͱ47(&YUSBDU ΖΖʜ
ʮΖΖʯ͕ॏཁ ͩͬͨΓ͢Δ
id - Inline SVG ʹ͓͚ΔidଐੑͷऔΓѻ͍ཁҙ - ߲લͷʮͱ͍͕͑ແ͍Θ͚Ͱͳ͍ʯͷ෦
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> <mask id="a" style="mask-type:
alpha” maskUnits="userSpaceOnUse" x="1" y="1" width="22" height="22"> <path fill-rule="evenodd" clip-rule="evenodd" d=“…” fill="#808D96" /> </mask> <g mask="url(#a)"> <path fill-rule="evenodd" clip-rule="evenodd" d=“…” fill="#808D96" /> </g> <path fill-rule="evenodd" clip-rule="evenodd" d=“…” - ΫϦοϐϯάϚεΫ͕ id + URL ܗࣜͰࢀর͞ΕΔ - Inline SVG ͱͯ͠ల։͢Δ߹ɺdocument શମͰৗʹ ҰҙͰ͋Δ͜ͱΛ୲อ͢Δඞཁ͕͋Δ - ( <label htmlFor=“…”> ͷѻ͍͕໘ͳͷͱಉ͡)
const id = useId() return ( <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0
0 24 24"> <mask id={id} style="mask-type: alpha” maskUnits="userSpaceOnUse" x="1" y="1" width="22" height="22"> <path fill-rule="evenodd" clip-rule="evenodd" d=“…” fill="#808D96" /> </mask> <g mask={`url(#${id})`}> <path fill-rule="evenodd" clip-rule="evenodd" d=“…” fill="#808D96" /> </g> <path fill-rule="evenodd" clip-rule="evenodd" d=“…” - idͷॏෳΛ͙ʹɺComponent ͷඳը࣌ʹಈతʹ ൃ൪͢Δඞཁ͕͋Δ ※ ಉ࣌ʹಉ͡ SVG ͕ෳඳը͞Εͨ߹ߟྀ͠ͳͯ͘ͳΒͳ͍ͨΊ - நग़ͨ͠SVG ʹ id ଐੑ/ࢀরؚ͕·Ε͍ͯΔ߹ɺ ֘ͷ id ଐੑΛJSXࣜʹஔ͢ΔରԠ͕ඞཁʹ
Figma to TSX (ࣗಈ) - Figma͔ΒSVGίʔυΛίϐϖ - svgo Ͱѹॖ, svg2jsx
Ͱ JSXԽ - ΤσΟλʹషΓ͚ͨޙɺΖΖΛ ͑ͯอଘ 'JHNB"1*Ͱ47(&YUSBDU JEͷ݅Ҏ֎ʹࡉʑͨ͠มߋ͕৭ʑඞཁ QBUIཁૉͷpMMଐੑΛDVSSFOU$PMPSʹ DMBTT/BNFଐੑΛՃ WJFX#PYΛἧ͑Δ FUD
AST Transformation ʮΖΖΛ͑ͯอଘʯΛݴ͍͑Δͱ: SVG ͷߏΛ୳ࡧͭͭ͠ɺඞཁʹԠͨ͡ஔॲஔ - AST(Abstract Syntax Tree) Transformation
ͦͷͷ - Babel swc ͕ߦ͍ͬͯΔ Transpile ॲཧ(.ts -> .js) ͱಉ͡ܥ౷ͷॲཧ
AST Transformation https://astexplorer.net
AST Transformation via TS - XML ͱͯ͠ͷมͰ͋ΕɺDOM APIͰे࣮ݱՄೳ (i.e. XSLT)
- ઌͷ URL referenceͳͲɺJSXࣜͱͳΔͱͦ͏͍͔ͳ͍ - ࣮ TypeScript ୯ମͰͰ͖ͯ͠·͏ - parse( string -> ts.Node) : ts.craeteSourceFile - ASTม(ts.Node -> ts.Node): ts.transform - unparse(ts.Node -> string): ts.createPrinter().printFile
AST Transformation via TS - TS Compiler APIʹΑΔ AST มίʔυ
- Try with Playground! export function transform(src: ts.Node) { const factory: ts.TransformerFactory<ts.Node> = (ctx) => { const visitor = (node: ts.Node): ts.Node | undefined => { // ͜͜ʹ node ʹର͢ΔॲཧΛॻ͍͍ͯ͘ } return (node) => ts.visitEachChild(node, visitor, ctx) } const result = ts.transform(src, [factory]) return result.transformed[0] }
͜͜·Ͱͷ·ͱΊ - Inline SVG Λඳը͢Δ React ComponentͰ Iconography TokenΛ࣮ݱ͠ ͍ͯΔΑ
- Iconography Token Component Figma API Λͬͯࣗಈੜ͍ͯ͠ ΔΑ - ͜·͝·ͨ͠ཁ͕݅৭ʑ͋Δ͚ͲɺAST มͰؤு͍ͬͯΔΑ
ͬͯͯྑ͔ͬͨࣗಈੜ - Ұൠతʹίʔυੜશൠʹݴ͑Δ͜ͱͰ͕͢ - ७ਮʹͷݮʹͭͳ͕Δ - ԿेݸReact ComponentԽͨ͠ޙͰमਖ਼͕༰қ - ϓϩδΣΫτΛΒͤͳ͕ΒɺϝϯόʔͱʮIconͬͺΓ͜͏͍
͏Propsʹͨ͠ํ͕Α͍ʯͷΑ͏ͳٞΛͯ͠௧Έ͕গͳ͍
Why AST? - ͜͜·ͰͷͰʮͦΕਖ਼نදݱͰेͰʯͱࢥͬͨਓ͍Δ͔͠Ε ͳ͍ - ASTૢ࡞Λ͍ͬͯΔͱϑϩϯτΤϯυ։ൃͷ༷ʑͳγʔϯͰ༗༻ - Project Specific
ͳ ESLint rule ࡞, webpack custom loader (AoT Compilation), etc…
Thank you !