vite-plugin-tres.ts 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. import { createUnplugin } from 'unplugin'
  2. import * as THREE from 'three'
  3. import fs from 'fs'
  4. import { join } from 'pathe'
  5. const blacklist = ['Scene', 'Color', 'Object3D']
  6. export const unplugin = createUnplugin(() => {
  7. let modules = []
  8. return {
  9. name: 'unplugin-tres',
  10. buildStart() {
  11. console.log('✨ Magically generating components...')
  12. const outputDir = join('.tres', 'components')
  13. if (!fs.existsSync(outputDir)) {
  14. fs.mkdirSync(outputDir, { recursive: true })
  15. }
  16. let indexTemplate = ''
  17. for (const key in THREE) {
  18. const value = (THREE as any)[key]
  19. if (
  20. blacklist.includes(key) ||
  21. key.includes('Vector') || // Vector2, Vector3, Vector4
  22. key.includes('BufferGeometry') || // Deprecated geometries
  23. key.includes('Utils') || // Utils
  24. key.includes('Curve') || // Curves
  25. key.includes('Audio') // Curves
  26. )
  27. continue
  28. if (typeof value === 'function' && /^\s*class\s+/.test(value.toString())) {
  29. const outputFilePath = join(outputDir, `${key}.ts`)
  30. const template = `
  31. import { defineComponent, inject, ShallowRef } from 'vue';
  32. import { ${key}, Scene, Color, Vector3, Object3D } from 'three';
  33. import { useCamera } from '/@/composables/'
  34. import { VectorFlexibleParams, normalizeVectorFlexibleParam, normalizeColor } from '/@/utils/normalize';
  35. let ${key}Instance: ${key};
  36. let instanceProps: string[] = [];
  37. /**
  38. * Tres${key}Props
  39. * @type {Partial<${key}> & {parentInstance?: ${key}}}
  40. * @memberof Tres${key}
  41. * @description This is a partial of the ${key} class, with the parentInstance property added.
  42. *
  43. **/
  44. export type Tres${key}Props = Partial<${key}> & {
  45. parentInstance?: ${key},
  46. /**
  47. *
  48. * Array of arguments to pass to the ${key} constructor
  49. *
  50. * @type {Array<any>}
  51. * @memberof Tres${key}Props
  52. * @see https://threejs.org/docs/?q=${key}
  53. *
  54. **/
  55. args?: Array<any>,
  56. /**
  57. *
  58. * Object's local position
  59. *
  60. * @type {VectorFlexibleParams}
  61. * @memberof Tres${key}Props
  62. **/
  63. position?: VectorFlexibleParams
  64. }
  65. try {
  66. // @ts-ignore
  67. ${key}Instance = new ${key}();
  68. instanceProps = [...Object.keys(${key}Instance)]
  69. } catch (e) {
  70. }
  71. export const Tres${key} = /* #__PURE__ */ defineComponent<Tres${key}Props>({
  72. name: 'Tres${key}',
  73. props: ['parentInstance', 'args', ...instanceProps] as unknown as undefined,
  74. setup(props, { slots, expose }) {
  75. if(props.args) {
  76. // @ts-ignore
  77. ${key}Instance = new ${key}(...props.args);
  78. } else {
  79. // @ts-ignore
  80. ${key}Instance = new ${key}();
  81. }
  82. const scene = inject<ShallowRef<Scene>>('scene')
  83. expose({${key}Instance})
  84. function append(parent, child) {
  85. const regex = /[A-Z][a-z]+/g
  86. const propName = child.type.match(regex).pop().toLowerCase()
  87. if (parent[propName]) {
  88. parent[propName] = child
  89. }
  90. }
  91. function processProps() {
  92. Object.keys(props).forEach((key) => {
  93. if (props[key] !== undefined && key !== 'parentInstance' && key !== 'args') {
  94. if( ${key}Instance[key] instanceof Color) {
  95. ${key}Instance[key] = normalizeColor(props[key])
  96. } else if ( ${key}Instance[key] instanceof Vector3) {
  97. ${key}Instance[key].set(normalizeVectorFlexibleParam(props[key]))
  98. } else {
  99. ${key}Instance[key] = props[key]
  100. }
  101. }
  102. })
  103. }
  104. processProps()
  105. if (${key}Instance.hasOwnProperty('isCamera')) {
  106. ${key}Instance.position.set(0, 0, 5)
  107. ${key}Instance.lookAt(0, 0, 0)
  108. const { pushCamera } = useCamera()
  109. pushCamera(${key}Instance)
  110. }
  111. if(props.parentInstance) {
  112. append(props.parentInstance, ${key}Instance)
  113. }
  114. if(scene && !props.parentInstance && ${key}Instance instanceof Object3D) {
  115. scene.value.add(${key}Instance)
  116. }
  117. const preparedSlots = slots.default
  118. // eslint-disable-next-line max-len
  119. ? slots.default().map((slot) => { slot.props = {
  120. ...slot.props,
  121. parentInstance: ${key}Instance };
  122. return slot;
  123. })
  124. : null
  125. return () => {
  126. return preparedSlots;
  127. };
  128. },
  129. });
  130. export default Tres${key};
  131. `
  132. indexTemplate += `export * from './${key}'\n`
  133. fs.writeFileSync(outputFilePath, template)
  134. modules.push(key)
  135. }
  136. fs.writeFileSync(join(outputDir, `index.ts`), indexTemplate)
  137. }
  138. console.log(`✨ Generated ${modules.length} components!`)
  139. },
  140. }
  141. })
  142. export const ViteTresPlugin = unplugin.vite