You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

generate.ts 3.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. import Rectangle from './rectangle'
  2. import Ellipse from './ellipse'
  3. import Polyline from './polyline'
  4. import Dot from './dot'
  5. import Ray from './ray'
  6. import Line from './line'
  7. import Arrow from './arrow'
  8. import Draw from './draw'
  9. import Text from './text'
  10. import Utils from './utils'
  11. import Vec from 'utils/vec'
  12. import { NumberControl, VectorControl, codeControls, controls } from './control'
  13. import { codeShapes } from './index'
  14. import {
  15. CodeControl,
  16. Data,
  17. Shape,
  18. DashStyle,
  19. ColorStyle,
  20. FontSize,
  21. SizeStyle,
  22. CodeError,
  23. } from 'types'
  24. import { getPage, getShapes } from 'utils'
  25. import { transform } from 'sucrase'
  26. import { getErrorWithLineAndColumn, getFormattedCode } from 'utils/code'
  27. const baseScope = {
  28. Dot,
  29. Ellipse,
  30. Ray,
  31. Line,
  32. Polyline,
  33. Rectangle,
  34. Vec,
  35. Utils,
  36. Arrow,
  37. Draw,
  38. Text,
  39. VectorControl,
  40. NumberControl,
  41. DashStyle,
  42. ColorStyle,
  43. SizeStyle,
  44. FontSize,
  45. }
  46. /**
  47. * Evaluate code, collecting generated shapes in the shape set. Return the
  48. * collected shapes as an array.
  49. * @param code
  50. */
  51. export async function generateFromCode(
  52. data: Data,
  53. code: string
  54. ): Promise<{
  55. shapes: Shape[]
  56. controls: CodeControl[]
  57. error: CodeError
  58. }> {
  59. codeControls.clear()
  60. codeShapes.clear()
  61. ;(window as any).isUpdatingCode = false
  62. ;(window as any).currentPageId = data.currentPageId
  63. const { currentPageId } = data
  64. const scope = { ...baseScope, controls, currentPageId }
  65. let generatedShapes: Shape[] = []
  66. let generatedControls: CodeControl[] = []
  67. let error: CodeError | null = null
  68. try {
  69. const formattedCode = getFormattedCode(code)
  70. const transformedCode = transform(formattedCode, {
  71. transforms: ['typescript'],
  72. })?.code
  73. new Function(...Object.keys(scope), `${transformedCode}`)(
  74. ...Object.values(scope)
  75. )
  76. const startingChildIndex =
  77. getShapes(data)
  78. .filter((shape) => shape.parentId === data.currentPageId)
  79. .sort((a, b) => a.childIndex - b.childIndex)[0]?.childIndex || 1
  80. generatedShapes = Array.from(codeShapes.values())
  81. .sort((a, b) => a.shape.childIndex - b.shape.childIndex)
  82. .map((instance, i) => ({
  83. ...instance.shape,
  84. isGenerated: true,
  85. parentId: getPage(data).id,
  86. childIndex: startingChildIndex + i,
  87. }))
  88. generatedControls = Array.from(codeControls.values())
  89. } catch (e) {
  90. error = getErrorWithLineAndColumn(e)
  91. }
  92. return { shapes: generatedShapes, controls: generatedControls, error }
  93. }
  94. /**
  95. * Evaluate code, collecting generated shapes in the shape set. Return the
  96. * collected shapes as an array.
  97. * @param code
  98. */
  99. export async function updateFromCode(
  100. data: Data,
  101. code: string
  102. ): Promise<{
  103. shapes: Shape[]
  104. }> {
  105. codeShapes.clear()
  106. ;(window as any).isUpdatingCode = true
  107. ;(window as any).currentPageId = data.currentPageId
  108. const { currentPageId } = data
  109. const newControls = Object.fromEntries(
  110. Object.entries(data.codeControls).map(([_, control]) => [
  111. control.label,
  112. control.value,
  113. ])
  114. )
  115. const scope = {
  116. ...baseScope,
  117. currentPageId,
  118. controls: newControls,
  119. }
  120. const startingChildIndex =
  121. getShapes(data)
  122. .filter((shape) => shape.parentId === data.currentPageId)
  123. .sort((a, b) => a.childIndex - b.childIndex)[0]?.childIndex || 1
  124. const transformed = transform(code, {
  125. transforms: ['typescript'],
  126. }).code
  127. new Function(...Object.keys(scope), `${transformed}`)(...Object.values(scope))
  128. const generatedShapes = Array.from(codeShapes.values())
  129. .sort((a, b) => a.shape.childIndex - b.shape.childIndex)
  130. .map((instance, i) => ({
  131. ...instance.shape,
  132. isGenerated: true,
  133. parentId: getPage(data).id,
  134. childIndex: startingChildIndex + i,
  135. }))
  136. return { shapes: generatedShapes }
  137. }