Save 37% off PRO during our Black Friday Sale! »

Generate React Component with TypeScript AST

893f54413c2bd9ba41d11d753aacaf2c?s=47 Yosuke Kurami
November 18, 2021

Generate React Component with TypeScript AST

893f54413c2bd9ba41d11d753aacaf2c?s=128

Yosuke Kurami

November 18, 2021
Tweet

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 !