1
0

sd.config.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. /* eslint-disable @typescript-eslint/no-var-requires */
  2. const { template, camelCase, cloneDeep } = require('lodash')
  3. const variablesTemplate = template(`import { css } from '@emotion/react'
  4. export const variables = css\`
  5. :root {
  6. <%= cssVariables %>
  7. }\`
  8. export const theme = {
  9. <%= themeVariables %>
  10. }
  11. export const cVar = (key: keyof typeof theme, returnValue?: boolean) => {
  12. if (returnValue) return theme[key].value
  13. return theme[key].variable
  14. }
  15. `)
  16. module.exports = {
  17. source: [`./src/styles/tokens/**/*.json`],
  18. parsers: [
  19. {
  20. pattern: /\.json$/,
  21. parse: ({ contents }) => {
  22. // style dictionary requires adding ".value" suffix to all referenced value, e.g. "value": "{core.neutral.default.900}" should be e.g. "value": "{core.neutral.default.900.value}"
  23. // this parser should fix it, although it just a workaround
  24. // we could remove this when they do something about it https://github.com/amzn/style-dictionary/issues/721
  25. const parsed = contents.replace(/}"|\.value}"/g, `.value}"`)
  26. return JSON.parse(parsed)
  27. },
  28. },
  29. // creates filterEffects design tokens, which are needed to use for `filter: drop-shadow()`
  30. {
  31. pattern: /\.json$/,
  32. parse: ({ contents }) => {
  33. const obj = JSON.parse(contents)
  34. if (obj.effect) {
  35. const deepCopy = cloneDeep(obj)
  36. deepCopy.filterEffect = deepCopy.effect
  37. delete deepCopy.effect
  38. return { ...obj, ...deepCopy }
  39. }
  40. },
  41. },
  42. ],
  43. transform: {
  44. removeDefaultFromName: {
  45. type: 'name',
  46. transformer: (token) => token.name.replace(/-default|-regular/g, ''),
  47. },
  48. easingTransform: {
  49. type: 'value',
  50. matcher: (token) => token.attributes.type === 'easing',
  51. // [1, 2, 3, 4] will become 1, 2, 3, 4
  52. transformer: (token) => `cubic-bezier(${token.value.toString().replace(/\[|\]/g, '')})`,
  53. },
  54. transitionTransform: {
  55. type: 'value',
  56. transitive: true,
  57. matcher: (token) => token.attributes.type === 'transition',
  58. transformer: (token) => `${token.value.timing} ${token.value.easing}`,
  59. },
  60. typographyValueTransform: {
  61. type: 'value',
  62. transitive: true,
  63. matcher: (token) => token.attributes.type === 'textStyles',
  64. transformer: (token) =>
  65. `${token.value.fontWeight} ${token.value.fontSize}/${token.value.lineHeight} ${token.value.fontFamily}`,
  66. },
  67. typographyNameTransform: {
  68. type: 'name',
  69. matcher: (token) => token.attributes.type === 'textStyles',
  70. transformer: (token) => token.name.replace(/-heading|-text|-styles/g, ''),
  71. },
  72. effectsTransform: {
  73. type: 'value',
  74. transitive: true,
  75. matcher: (token) => token.attributes.category === 'effect',
  76. transformer: (token) => {
  77. const isDivider = token.attributes.type === 'dividers'
  78. return `${isDivider ? 'inset' : ''} ${token.value.x} ${token.value.y} ${token.value.blur} ${
  79. token.value.spread
  80. } ${token.value.color}`
  81. },
  82. },
  83. filterEffectsTransform: {
  84. type: 'value',
  85. transitive: true,
  86. matcher: (token) => token.attributes.category === 'filterEffect',
  87. transformer: (token) => `drop-shadow(${token.value.x} ${token.value.y} ${token.value.blur} ${token.value.color})`,
  88. },
  89. },
  90. format: {
  91. customFormat: ({ dictionary }) => {
  92. // create new tokens for letter-spacing
  93. const allTokens = dictionary.allTokens
  94. const letterSpacingTokens = allTokens
  95. .filter((token) => token.attributes.type === 'textStyles')
  96. .map((token) => ({
  97. ...token,
  98. value: token.original.value.letterSpacing || 0,
  99. name: `${token.name}-letter-spacing`,
  100. }))
  101. // create new tokens for text-transform
  102. const textTransformTokens = allTokens
  103. .filter((token) => token.attributes.type === 'textStyles')
  104. .map((token) => ({
  105. ...token,
  106. value: token.original.value.textTransform || 'none',
  107. name: `${token.name}-text-transform`,
  108. }))
  109. const convertedTokens = [...allTokens, ...letterSpacingTokens, ...textTransformTokens]
  110. return variablesTemplate({
  111. cssVariables: convertedTokens
  112. .map((token) => {
  113. let keyValuePair = `--${token.name}: ${token.value};`
  114. if (dictionary.usesReference(token.original.value)) {
  115. const refs = dictionary.getReferences(token.original.value)
  116. refs.forEach((ref) => {
  117. const [key, value] = keyValuePair.split(':')
  118. const modifiedValue = value.replace(ref.value, `var(--${ref.name})`)
  119. keyValuePair = `${key}:${modifiedValue}`
  120. })
  121. }
  122. return keyValuePair
  123. })
  124. .join('\n'),
  125. themeVariables: convertedTokens
  126. .map((token) => `${camelCase(token.name)}: { variable: 'var(--${token.name})', value: '${token.value}' },`)
  127. .join('\n'),
  128. })
  129. },
  130. },
  131. platforms: {
  132. ts: {
  133. transforms: [
  134. `attribute/cti`,
  135. `name/cti/kebab`,
  136. 'removeDefaultFromName',
  137. 'typographyValueTransform',
  138. 'typographyNameTransform',
  139. 'easingTransform',
  140. 'transitionTransform',
  141. 'effectsTransform',
  142. 'filterEffectsTransform',
  143. ],
  144. buildPath: 'src/styles/generated/',
  145. files: [
  146. {
  147. destination: 'variables.ts',
  148. format: 'customFormat',
  149. filter: (token) => token.type !== 'typedef',
  150. options: {
  151. outputReferences: true,
  152. },
  153. },
  154. ],
  155. },
  156. },
  157. }