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.8k
App Router 悲喜交々
quramy
8
740
上手に付き合うコンポーネントテスト
quramy
6
2.4k
Patched fetch did not work
quramy
6
790
GraphQL あるいは React における自律的なデータ取得について
quramy
18
5.9k
Next.js App Router
quramy
15
3.9k
Fragment Composition of GraphQL
quramy
17
4.8k
reg-viz VRT tools
quramy
4
1.7k
Other Decks in Programming
See All in Programming
Apache Hive: そしてCloud Native Lakehouseへ
okumin
1
150
エンジニア向け会社紹介/Findy Company Profile
findyinc
6
360k
ソフトウェア設計に溶けるインフラ ― AWS CDK のインフラ認識論
konokenj
2
600
AI時代の仕事技芸論〜ソフトウェア開発で「遊ぶように働く」職人的熟達のすすめ(スクフェス仙台 2026バージョン)
kuranuki
0
680
Claude Team Plan導入・ガイド
tk3fftk
0
220
鹿野さんに聞く!『TypeScriptコードレシピ集』で磨く実践力
tonkotsuboy_com
4
1.1k
【やさしく解説 設計編・中級 #6】良いアーキテクチャとは ~ 一本の登り道の、行き先 ~
panda728
PRO
0
170
yield再入門 #phpcon
o0h
PRO
0
670
The Bowling Game- From Imperative to Functional Programming - Part 1
philipschwarz
PRO
0
340
Generative UI & AI-Assistants for Your Angular Solutions
manfredsteyer
PRO
0
120
使用 Meilisearch 建立新聞搜尋工具
johnroyer
0
160
信頼性について考えてみる(SRE NEXT 2026 miniLT)
hayama17
0
200
Featured
See All Featured
Crafting Experiences
bethany
1
230
Effective software design: The role of men in debugging patriarchy in IT @ Voxxed Days AMS
baasie
0
450
Amusing Abliteration
ianozsvald
1
230
Learning to Love Humans: Emotional Interface Design
aarron
275
41k
First, design no harm
axbom
PRO
2
1.2k
Building Better People: How to give real-time feedback that sticks.
wjessup
370
20k
Designing for Performance
lara
611
70k
Side Projects
sachag
455
43k
Chasing Engaging Ingredients in Design
codingconduct
0
240
WCS-LA-2024
lcolladotor
0
730
How STYLIGHT went responsive
nonsquared
100
6.2k
Measuring & Analyzing Core Web Vitals
bluesmoon
9
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 !