$30 off During Our Annual Pro Sale. View Details »

Generate React Component with TypeScript AST

Yosuke Kurami
November 18, 2021

Generate React Component with TypeScript AST

Yosuke Kurami

November 18, 2021
Tweet

More Decks by Yosuke Kurami

Other Decks in Programming

Transcript

  1. Generate React Component with TypeScript AST 2021.11.18

  2. About me - Twitter/GitHub: @Quramy - ϦΫϧʔτͰWebϑϩϯτΤϯδχΞ΍ͬͯ·͢ - ࠓ೥ͷ7݄ࠒ͔ΒελσΟαϓϦͷ࢓ࣄΛ͢ΔΑ͏ ʹͳΓ·ͨ͠

  3. ݸਓతͳ 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Ͱѻ͏ͨΊͷϢʔςΟϦςΟ
  4. ݸਓతͳ 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ʹภ͍ͬͯΔ
  5. ࠓ೔࿩͢͜ͱ - ࠓ೔΋ࠓ೔ͱͯ TypeScript ͷ࿩ - QuramyࢀըதͷελσΟαϓϦϓϩδΣΫτ: Next.js + TypeScript

    + GraphQL ͳ ΞϓϦέʔγϣϯ ※ Web෦෼ʹݶΔ. ผ్ωΠςΟϒApp΋͋Δ - ϓϩδΣΫτͰ࣮ࢪ͍ͯ͠Δ Figma -> React Component (TSX) ͷࣗ ಈੜ੒෦෼ʹ͍ͭͯ঺հ
  6. Agenda 1. σβΠϯγεςϜͱ Iconography Token 2. Iconography React Component ͷࣗಈੜ੒

  7. Agenda 1. σβΠϯγεςϜͱ Iconography Token 2. Iconography React Component ͷࣗಈੜ੒

  8. σβΠϯγεςϜ - ݱࡏࢀը͍ͯ͠ΔϓϩδΣΫτͷσβΠϯγεςϜͷಛ௃ - Designer ͕ Figma Ͱ؅ཧ ※ Figma

    ʹରԠ͢Δ࣮૷ʹ͍ͭͯ͸ Web Devs / Native app Devs ͷ੹຿ - ίϯϙʔωϯτࢤ޲ - Atomic Design ϕʔε
  9. “Extended” Atomic Design https://bradfrost.com/blog/post/extending-atomic-design/

  10. Design Token ͱ͸ - ͦΕࣗ਎͸UIཁૉʹͳΓ͑ͳ͍͕ɺͦΕͳͯ͘͠σβΠϯγεςϜΛ࣮ ݱͰ͖ͳ͍ߏ੒෺ͷ͜ͱ - e.g. ৭ม਺΍λΠϙάϥϑΟͳͲ -

    Extended Atomic Design Ͱ͸ Atoms ΑΓ΋ͳ͓ԼҐͷଘࡏ - (༨ஊ) ߏจղੳͰ΋ʮ͜ΕҎ্෼ղෆՄೳͳจࣈྻʯͱ͍͏ҙຯͰ token ͱ͍͏ݴ༿͕Ͱͯ͘Δ
  11. Design Tokenͷҙٛ - Token ͸ϓϥοτϑΥʔϜʹґଘ͠ͳ͍ ϓϩμΫτσβΠϯͷࠜݩ - TokenΛϓϥοτϑΥʔϜݻ༗ͷݴޠ (React ΍

    Kotlin) ʹ຋༁ɾม׵ͯ͠ར༻͢Δ͜ͱͰɺ Ұ؏ੑͷ͋ΔσβΠϯͱͳΔ - ࣗಈੜ੒ͱ૬ੑ͕ྑ͍ UI Component for PC Web UI Component for Android Token Automatic Compilation
  12. Token Kinds Web ΞϓϦέʔγϣϯʹ͓͚Δ Design Token ࣮ݱखஈ(Ұྫ) छྨ ࣮૷ܗଶ $PMPS4IBEPX(VUUFS

    $44$VTUPN1SPQFSUJFT PS +BWB4DSJQUDPOTUBOU $44JO+4 5ZQPHSBQIZ $44!GPOUGBDF  MJOFIFJHIUGPOUTJ[FQSPQT *DPOPHSBQIZ 47(pMFPS*OMJOF47(PS8FC'POUTFUD
  13. Token Kinds Web ΞϓϦέʔγϣϯʹ͓͚Δ Design Token ࣮ݱखஈ(Ұྫ) छྨ ࣮૷ܗଶ $PMPS4IBEPX(VUUFS

    $44$VTUPN1SPQFSUJFT PS +BWB4DSJQUDPOTUBOU $44JO+4 5ZQPHSBQIZ $44!GPOUGBDF  MJOFIFJHIUGPOUTJ[FQSPQT *DPOPHSBQIZ 47(pMFPS*OMJOF47(PS8FC'POUTFUD
  14. Iconography Token - ৭ม਺΍ϑΥϯτάϦϑͱಉ༷ʹΞΠίϯΛߏ੒͢Δύε৘ใ΋ Design Token - ϓϩδΣΫτͰ͸ɺWeb Ͱͷ Token࣮૷ܗଶͱͯ͠

    Inline SVGΛબ୒ - Atoms Component͔Β࢖͍΍͍͢Α͏ʹɺIconography Tokenࣗମ΋ React Componentͱ࣮ͯ͠૷
  15. Iconography Token via React - ྫ: υϩοϓμ΢ϯϦετ - ҙຯΛ࣋ͭ࠷খUI୯Ґ͸ υϩοϓμ΢ϯ

    ▼ ෦෼ͷ SVG ୯ମʹҙຯ͸ແ͍ Iconography Token Dropdown Component (Atoms) import { Down } from "../../tokens/Iconography/Common" export function Dropdown() { return ( <> {/* தུ */} <Down /> </> ) }
  16. Agenda 1. σβΠϯγεςϜͱ Iconography Token 2. Iconography React Component ͷࣗಈੜ੒

  17. PC Web Devs workflow ϓϩδΣΫτͰͷେ·͔ͳը໘։ൃϫʔΫϑϩʔ  'JHNBͰ6*694QFD֬ೝ  3FBDU54Ͱ࣮૷ 

    4UPSZCPPLͰ֬ೝςετ
  18. None
  19. ίΠπϥΛΰχϣΰχϣͯ͠ 3FBDU$PNQPOFOUʹͨ͠Ε

  20. 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 ͷத਎
  21. Why inline SVG? ଞํࣜͱൺֱ͢ΔͱɺInline SVG ͕ Icon Tokenͷ࣮૷ʹ࠷΋ద͍ͯ͠Δ ํ๏ QSPTDPOT

    JNH 47(pMF  'JHNB͔ΒҰׅͰ47(ϑΝΠϧΛFYQPSU͢Δ͚ͩ  ৭Λมߋ͢Δͷ͕༰қͰ͸ͳ͍ %FTJHO5PLFOͱͯ͠க໋త 8FC'POUT  ७ਮʹ໘౗  ςΩετͱͯ͠ղऍ͞ΕΔͨΊɺҙਤͤ͵࠷దԽ͕ͳ͞ΕΔ ಛʹXFCLJUܥ *OMJOF47( 3FBDU$PNQPOFOU  5SFF4IBLBCMF  ͱ͸͍͑໰୊͕ແ͍Θ͚Ͱ͸ͳ͍˞ޙड़
  22. Figma to TSX (खಈ) - Figma͔ΒSVGίʔυΛίϐϖ - svgo Ͱѹॖ, svg2jsx

    Ͱ JSXԽ - ΤσΟλʹషΓ෇͚ͨޙɺ΋Ζ΋ΖΛ੔ ͑ͯอଘ
  23. Figma to TSX (ࣗಈ?) - Figma͔ΒSVGίʔυΛίϐϖ - svgo Ͱѹॖ, svg2jsx

    Ͱ JSXԽ - ΤσΟλʹషΓ෇͚ͨޙɺ΋Ζ΋ΖΛ੔ ͑ͯอଘ 'JHNB"1*Ͱ47(&YUSBDU ΋Ζ΋Ζʜ
  24. ʮ΋Ζ΋Ζʯ͕ॏཁ ͩͬͨΓ͢Δ

  25. id໰୊ - Inline SVG ʹ͓͚ΔidଐੑͷऔΓѻ͍͸ཁ஫ҙ - ਺߲લͷʮͱ͸͍͑໰୊͕ແ͍Θ͚Ͱ͸ͳ͍ʯͷ෦෼

  26. <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=“…”> ͷѻ͍͕໘౗ͳͷͱಉ͡)
  27. 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ࣜʹஔ׵͢ΔରԠ͕ඞཁʹ
  28. Figma to TSX (ࣗಈ) - Figma͔ΒSVGίʔυΛίϐϖ - svgo Ͱѹॖ, svg2jsx

    Ͱ JSXԽ - ΤσΟλʹషΓ෇͚ͨޙɺ΋Ζ΋ΖΛ੔ ͑ͯอଘ 'JHNB"1*Ͱ47(&YUSBDU JEͷ݅Ҏ֎ʹ΋ࡉʑͨ͠มߋ͕৭ʑඞཁ  QBUIཁૉͷpMMଐੑΛDVSSFOU$PMPSʹ  DMBTT/BNFଐੑΛ௥Ճ  WJFX#PYΛἧ͑Δ  FUD
  29. AST Transformation ʮ΋Ζ΋ΖΛ੔͑ͯอଘʯΛݴ͍׵͑Δͱ: SVG ͷ໦ߏ଄Λ୳ࡧͭͭ͠ɺඞཁʹԠͨ͡ஔ׵ॲஔ - AST(Abstract Syntax Tree) Transformation

    ͦͷ΋ͷ - Babel ΍ swc ͕ߦ͍ͬͯΔ Transpile ॲཧ(.ts -> .js) ͱಉ͡ܥ౷ͷॲཧ
  30. AST Transformation https://astexplorer.net

  31. 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
  32. 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] }
  33. ͜͜·Ͱͷ·ͱΊ - Inline SVG Λඳը͢Δ React ComponentͰ Iconography TokenΛ࣮ݱ͠ ͍ͯΔΑ

    - Iconography Token Component ͸ Figma API Λ࢖ͬͯࣗಈੜ੒͍ͯ͠ ΔΑ - ͜·͝·ͨ͠ཁ͕݅৭ʑ͋Δ͚ͲɺAST ม׵Ͱؤு͍ͬͯΔΑ
  34. ΍ͬͯͯྑ͔ͬͨࣗಈੜ੒ - Ұൠతʹίʔυੜ੒શൠʹݴ͑Δ͜ͱͰ͕͢ - ७ਮʹ޻਺ͷ࡟ݮʹͭͳ͕Δ - Կेݸ΋React ComponentԽͨ͠ޙͰ΋मਖ਼͕༰қ - ϓϩδΣΫτΛ૸Βͤͳ͕ΒɺϝϯόʔͱʮIcon͸΍ͬͺΓ͜͏͍

    ͏Propsʹͨ͠ํ͕Α͍ʯͷΑ͏ͳٞ࿦Λͯ͠΋௧Έ͕গͳ͍
  35. Why AST? - ͜͜·Ͱͷ࿩ͰʮͦΕਖ਼نදݱͰे෼Ͱ͸ʯͱࢥͬͨਓ΋͍Δ͔΋͠Ε ͳ͍ - ASTૢ࡞Λ஌͍ͬͯΔͱϑϩϯτΤϯυ։ൃͷ༷ʑͳγʔϯͰ༗༻ - Project Specific

    ͳ ESLint rule ࡞੒, webpack custom loader (AoT Compilation), etc…
  36. Thank you !