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
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
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
Jindong: Introducing Declarative Haptics in Compose Multiplatform
l2hyunwoo
0
110
関東Kaggler会_NVIDIA_Nemotron_コンペ_振り返り
rick_ds
0
550
freeeにおけるEvalsの実践例の紹介
freee
PRO
0
110
Building a Meta Ray-Ban display app
akkeylab
0
130
Claude Code全社展開のためにやったことn選~プラグイン302個・コミッター271人を支えるために~
kenchan
5
1.5k
AI Readyの正体はデータマネジメントだ メダリオン2.0の最前線
freee
PRO
0
210
PHP初心者セッション2026 〜生成AIでは見えない裏側を知る:今だからLAMPを通して仕組みを学ぶ〜
kashioka
0
910
Laravelで学ぶ Webアプリケーションチューニング入門/web_application_tuning_101
hanhan1978
4
1.8k
PostgreSQL 18で考えるUUID主キー
kazuhiro1982
0
470
ドリフトを絶対に許さない(?)CDK運用 / CDK Ops with Zero Tolerance for Drifts (?)
akihisaikeda
1
180
PHP に部分適用が来るぞ!……ところで何それ?おいしいの? #phpcon / phpcon-2026
shogogg
0
640
仕様駆動開発の消費期限
watany
20
8.2k
Featured
See All Featured
Code Review Best Practice
trishagee
74
20k
The SEO Collaboration Effect
kristinabergwall1
1
520
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
35
3.7k
First, design no harm
axbom
PRO
2
1.2k
Have SEOs Ruined the Internet? - User Awareness of SEO in 2025
akashhashmi
0
410
Odyssey Design
rkendrick25
PRO
2
750
The #1 spot is gone: here's how to win anyway
tamaranovitovic
3
1.1k
Marketing to machines
jonoalderson
1
5.7k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
46
2.9k
Bootstrapping a Software Product
garrettdimon
PRO
307
120k
Un-Boring Meetings
codingconduct
0
380
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
56
3.4k
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 !