Browse Source

Merge branch 'next' into feature/922-use-texture

alvarosabu 5 months ago
parent
commit
396857840c

+ 1 - 1
docs/cookbook/tweakpane.md

@@ -8,7 +8,7 @@ difficulty: 0
 
 # Tweakpane
 
-To make it easier to control the parameters of your scene, you can use [Tweakpane](https://cocopon.github.io/tweakpane/). In this guide, we will show you how to use Tweakpane to control the parameters of your scene.
+To make it easier to control the parameters of your scene, you can use [Tweakpane](https://tweakpane.github.io/docs/). In this guide, we will show you how to use Tweakpane to control the parameters of your scene.
 
 <StackBlitzEmbed project-id="tweakpane" />
 

+ 2 - 1
docs/de/directives/v-light-helper.md

@@ -12,7 +12,8 @@ Die folgenden Lichter werden unterstützt:
 
 ```vue{2,8,11,14,17}
 <script setup lang="ts">
-import { OrbitControls, Sphere, vLightHelper } from '@tresjs/cientos'
+import { vLightHelper } from '@tresjs/core'
+import { OrbitControls, Sphere } from '@tresjs/cientos'
 </script>
 <template>
   <TresCanvas >

+ 2 - 3
package.json

@@ -2,7 +2,7 @@
   "name": "@tresjs/core",
   "type": "module",
   "version": "4.3.3",
-  "packageManager": "pnpm@9.15.5",
+  "packageManager": "pnpm@10.6.3",
   "description": "Declarative ThreeJS using Vue Components",
   "author": "Alvaro Saburido <hola@alvarosaburido.dev> (https://github.com/alvarosabu/)",
   "license": "MIT",
@@ -21,8 +21,7 @@
   "exports": {
     ".": {
       "types": "./dist/index.d.ts",
-      "import": "./dist/tres.js",
-      "require": "./dist/tres.umd.cjs"
+      "import": "./dist/tres.js"
     },
     "./components": {
       "types": "./dist/src/components/index.d.ts"

+ 90 - 14
pnpm-lock.yaml

@@ -181,7 +181,7 @@ importers:
         version: 0.2.1(tweakpane@4.0.5)
       unplugin-auto-import:
         specifier: ^19.0.0
-        version: 19.1.0(@nuxt/kit@3.15.4(magicast@0.3.5))(@vueuse/core@12.7.0(typescript@5.7.3))
+        version: 19.1.1(@nuxt/kit@3.15.4(magicast@0.3.5))(@vueuse/core@12.8.2(typescript@5.7.3))
       vite-plugin-glsl:
         specifier: ^1.3.1
         version: 1.3.1(rollup@4.34.8)(vite@6.1.1(@types/node@22.13.5)(jiti@2.4.2)(tsx@4.19.3)(yaml@2.7.0))
@@ -1767,6 +1767,9 @@ packages:
   '@types/web-bluetooth@0.0.20':
     resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==}
 
+  '@types/web-bluetooth@0.0.21':
+    resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==}
+
   '@types/webxr@0.5.21':
     resolution: {integrity: sha512-geZIAtLzjGmgY2JUi6VxXdCrTb99A7yP49lxLr2Nm/uIK0PkkxcEi4OGhoGDO4pxCf3JwGz2GiJL2Ej4K2bKaA==}
 
@@ -2090,6 +2093,9 @@ packages:
   '@vueuse/core@12.7.0':
     resolution: {integrity: sha512-jtK5B7YjZXmkGNHjviyGO4s3ZtEhbzSgrbX+s5o+Lr8i2nYqNyHuPVOeTdM1/hZ5Tkxg/KktAuAVDDiHMraMVA==}
 
+  '@vueuse/core@12.8.2':
+    resolution: {integrity: sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==}
+
   '@vueuse/integrations@12.7.0':
     resolution: {integrity: sha512-IEq7K4bCl7mn3uKJaWtNXnd1CAPaHLUMuyj5K1/k/pVcItt0VONZW8xiGxdIovJcQjkzOHjImhX5t6gija+0/g==}
     peerDependencies:
@@ -2137,6 +2143,9 @@ packages:
   '@vueuse/metadata@12.7.0':
     resolution: {integrity: sha512-4VvTH9mrjXqFN5LYa5YfqHVRI6j7R00Vy4995Rw7PQxyCL3z0Lli86iN4UemWqixxEvYfRjG+hF9wL8oLOn+3g==}
 
+  '@vueuse/metadata@12.8.2':
+    resolution: {integrity: sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==}
+
   '@vueuse/motion@2.2.6':
     resolution: {integrity: sha512-gKFktPtrdypSv44SaW1oBJKLBiP6kE5NcoQ6RsAU3InemESdiAutgQncfPe/rhLSLCtL4jTAhMmFfxoR6gm5LQ==}
     peerDependencies:
@@ -2148,6 +2157,9 @@ packages:
   '@vueuse/shared@12.7.0':
     resolution: {integrity: sha512-coLlUw2HHKsm7rPN6WqHJQr18WymN4wkA/3ThFaJ4v4gWGWAQQGK+MJxLuJTBs4mojQiazlVWAKNJNpUWGRkNw==}
 
+  '@vueuse/shared@12.8.2':
+    resolution: {integrity: sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==}
+
   '@webgpu/types@0.1.54':
     resolution: {integrity: sha512-81oaalC8LFrXjhsczomEQ0u3jG+TqE6V9QHLA8GNZq/Rnot0KDugu3LhSYSlie8tSdooAN1Hov05asrUUp9qgg==}
 
@@ -2165,6 +2177,11 @@ packages:
     engines: {node: '>=0.4.0'}
     hasBin: true
 
+  acorn@8.14.1:
+    resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==}
+    engines: {node: '>=0.4.0'}
+    hasBin: true
+
   add-stream@1.0.0:
     resolution: {integrity: sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==}
 
@@ -2497,6 +2514,9 @@ packages:
   confbox@0.1.8:
     resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==}
 
+  confbox@0.2.1:
+    resolution: {integrity: sha512-hkT3yDPFbs95mNCy1+7qNKC6Pro+/ibzYxtM2iqEigpf0sVw+bg4Zh9/snjsBcf990vfIsg5+1U7VyiyBb3etg==}
+
   config-chain@1.1.13:
     resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==}
 
@@ -3112,6 +3132,9 @@ packages:
     resolution: {integrity: sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==}
     engines: {node: '>=12.0.0'}
 
+  exsolve@1.0.4:
+    resolution: {integrity: sha512-xsZH6PXaER4XoV+NiT7JHp1bJodJVT+cxeSH1G0f0tlT0lJqYuHUP3bUx2HtfTDvOagMINYp8rsqusxud3RXhw==}
+
   external-editor@3.1.0:
     resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==}
     engines: {node: '>=4'}
@@ -3763,6 +3786,10 @@ packages:
     resolution: {integrity: sha512-bbgPw/wmroJsil/GgL4qjDzs5YLTBMQ99weRsok1XCDccQeehbHA/I1oRvk2NPtr7KGZgT/Y5tPRnAtMqeG2Kg==}
     engines: {node: '>=14'}
 
+  local-pkg@1.1.1:
+    resolution: {integrity: sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==}
+    engines: {node: '>=14'}
+
   locate-path@5.0.0:
     resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
     engines: {node: '>=8'}
@@ -4354,6 +4381,9 @@ packages:
   pkg-types@1.3.1:
     resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
 
+  pkg-types@2.1.0:
+    resolution: {integrity: sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A==}
+
   pluralize@8.0.0:
     resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
     engines: {node: '>=4'}
@@ -4420,6 +4450,9 @@ packages:
     resolution: {integrity: sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==}
     hasBin: true
 
+  quansync@0.2.8:
+    resolution: {integrity: sha512-4+saucphJMazjt7iOM27mbFCk+D9dd/zmgMDCzRZ8MEoBfYp7lAvoN38et/phRQF6wOPMy/OROBGgoWeSKyluA==}
+
   queue-microtask@1.2.3:
     resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
 
@@ -5095,8 +5128,8 @@ packages:
       vite:
         optional: true
 
-  unplugin-auto-import@19.1.0:
-    resolution: {integrity: sha512-B+TGBEBHqY9aR+7YfShfLujETOHstzpV+yaqgy5PkfV0QG7Py+TYMX7vJ9W4SrysHR+UzR+gzcx/nuZjmPeclA==}
+  unplugin-auto-import@19.1.1:
+    resolution: {integrity: sha512-sCGZZrSR1Bc8RfN8Q0RUDxXtC20rdAt7UB4lDyq8MNtKVHiXXh+5af6Nz4JRp9Q+7HjnbgQfQox0TkEymjdUAQ==}
     engines: {node: '>=14'}
     peerDependencies:
       '@nuxt/kit': ^3.2.2
@@ -6357,7 +6390,7 @@ snapshots:
       debug: 4.4.0
       globals: 15.15.0
       kolorist: 1.8.0
-      local-pkg: 1.0.0
+      local-pkg: 1.1.1
       mlly: 1.7.4
     transitivePeerDependencies:
       - supports-color
@@ -6949,7 +6982,7 @@ snapshots:
     dependencies:
       '@alvarosabu/utils': 3.2.0
       '@vue/devtools-api': 6.6.4
-      '@vueuse/core': 12.7.0(typescript@5.7.3)
+      '@vueuse/core': 12.8.2(typescript@5.7.3)
       three: 0.173.0
       vue: 3.5.13(typescript@5.7.3)
     transitivePeerDependencies:
@@ -7076,6 +7109,8 @@ snapshots:
 
   '@types/web-bluetooth@0.0.20': {}
 
+  '@types/web-bluetooth@0.0.21': {}
+
   '@types/webxr@0.5.21': {}
 
   '@typescript-eslint/eslint-plugin@8.24.1(@typescript-eslint/parser@8.24.1(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3)':
@@ -7612,6 +7647,15 @@ snapshots:
     transitivePeerDependencies:
       - typescript
 
+  '@vueuse/core@12.8.2(typescript@5.7.3)':
+    dependencies:
+      '@types/web-bluetooth': 0.0.21
+      '@vueuse/metadata': 12.8.2
+      '@vueuse/shared': 12.8.2(typescript@5.7.3)
+      vue: 3.5.13(typescript@5.7.3)
+    transitivePeerDependencies:
+      - typescript
+
   '@vueuse/integrations@12.7.0(focus-trap@7.6.4)(typescript@5.7.3)':
     dependencies:
       '@vueuse/core': 12.7.0(typescript@5.7.3)
@@ -7626,6 +7670,8 @@ snapshots:
 
   '@vueuse/metadata@12.7.0': {}
 
+  '@vueuse/metadata@12.8.2': {}
+
   '@vueuse/motion@2.2.6(magicast@0.3.5)(vue@3.5.13(typescript@5.7.3))':
     dependencies:
       '@vueuse/core': 10.11.1(vue@3.5.13(typescript@5.7.3))
@@ -7655,6 +7701,12 @@ snapshots:
     transitivePeerDependencies:
       - typescript
 
+  '@vueuse/shared@12.8.2(typescript@5.7.3)':
+    dependencies:
+      vue: 3.5.13(typescript@5.7.3)
+    transitivePeerDependencies:
+      - typescript
+
   '@webgpu/types@0.1.54': {}
 
   abbrev@3.0.0: {}
@@ -7663,8 +7715,14 @@ snapshots:
     dependencies:
       acorn: 8.14.0
 
+  acorn-jsx@5.3.2(acorn@8.14.1):
+    dependencies:
+      acorn: 8.14.1
+
   acorn@8.14.0: {}
 
+  acorn@8.14.1: {}
+
   add-stream@1.0.0: {}
 
   agent-base@7.1.3: {}
@@ -8008,6 +8066,8 @@ snapshots:
 
   confbox@0.1.8: {}
 
+  confbox@0.2.1: {}
+
   config-chain@1.1.13:
     dependencies:
       ini: 1.3.8
@@ -8723,8 +8783,8 @@ snapshots:
 
   espree@9.6.1:
     dependencies:
-      acorn: 8.14.0
-      acorn-jsx: 5.3.2(acorn@8.14.0)
+      acorn: 8.14.1
+      acorn-jsx: 5.3.2(acorn@8.14.1)
       eslint-visitor-keys: 3.4.3
 
   esprima@4.0.1: {}
@@ -8776,6 +8836,8 @@ snapshots:
 
   expect-type@1.1.0: {}
 
+  exsolve@1.0.4: {}
+
   external-editor@3.1.0:
     dependencies:
       chardet: 0.7.0
@@ -9396,7 +9458,7 @@ snapshots:
 
   jsonc-eslint-parser@2.4.0:
     dependencies:
-      acorn: 8.14.0
+      acorn: 8.14.1
       eslint-visitor-keys: 3.4.3
       espree: 9.6.1
       semver: 7.7.1
@@ -9448,6 +9510,12 @@ snapshots:
       mlly: 1.7.4
       pkg-types: 1.3.1
 
+  local-pkg@1.1.1:
+    dependencies:
+      mlly: 1.7.4
+      pkg-types: 2.1.0
+      quansync: 0.2.8
+
   locate-path@5.0.0:
     dependencies:
       p-locate: 4.1.0
@@ -10197,6 +10265,12 @@ snapshots:
       mlly: 1.7.4
       pathe: 2.0.3
 
+  pkg-types@2.1.0:
+    dependencies:
+      confbox: 0.2.1
+      exsolve: 1.0.4
+      pathe: 2.0.3
+
   pluralize@8.0.0: {}
 
   popmotion@11.0.5:
@@ -10262,6 +10336,8 @@ snapshots:
 
   qrcode-terminal@0.12.0: {}
 
+  quansync@0.2.8: {}
+
   queue-microtask@1.2.3: {}
 
   rc9@2.1.2:
@@ -10918,7 +10994,7 @@ snapshots:
 
   unctx@2.4.1:
     dependencies:
-      acorn: 8.14.0
+      acorn: 8.14.1
       estree-walker: 3.0.3
       magic-string: 0.30.17
       unplugin: 2.2.0
@@ -10938,10 +11014,10 @@ snapshots:
 
   unimport@4.1.2:
     dependencies:
-      acorn: 8.14.0
+      acorn: 8.14.1
       escape-string-regexp: 5.0.0
       estree-walker: 3.0.3
-      local-pkg: 1.0.0
+      local-pkg: 1.1.1
       magic-string: 0.30.17
       mlly: 1.7.4
       pathe: 2.0.3
@@ -11013,9 +11089,9 @@ snapshots:
       - supports-color
       - vue
 
-  unplugin-auto-import@19.1.0(@nuxt/kit@3.15.4(magicast@0.3.5))(@vueuse/core@12.7.0(typescript@5.7.3)):
+  unplugin-auto-import@19.1.1(@nuxt/kit@3.15.4(magicast@0.3.5))(@vueuse/core@12.8.2(typescript@5.7.3)):
     dependencies:
-      local-pkg: 1.0.0
+      local-pkg: 1.1.1
       magic-string: 0.30.17
       picomatch: 4.0.2
       unimport: 4.1.2
@@ -11023,7 +11099,7 @@ snapshots:
       unplugin-utils: 0.2.4
     optionalDependencies:
       '@nuxt/kit': 3.15.4(magicast@0.3.5)
-      '@vueuse/core': 12.7.0(typescript@5.7.3)
+      '@vueuse/core': 12.8.2(typescript@5.7.3)
 
   unplugin-utils@0.2.4:
     dependencies:

+ 0 - 1
src/composables/index.ts

@@ -4,7 +4,6 @@ import UsePBRTexture from './usePBRTexture/component.vue'
 
 export * from './useCamera/'
 export * from './useLoader'
-export * from './useLogger'
 export * from './useLoop'
 export * from './usePBRTexture'
 export * from './useRaycaster'

+ 1 - 1
src/composables/useCamera/index.ts

@@ -4,7 +4,7 @@ import type { TresContext } from '../useTresContextProvider'
 
 import { Camera, PerspectiveCamera } from 'three'
 import { computed, onUnmounted, ref, watchEffect } from 'vue'
-import { camera as isCamera } from '../../utils/is'
+import { isCamera } from '../../utils/is'
 
 export const useCamera = ({ sizes }: Pick<TresContext, 'sizes'> & { scene: TresScene }) => {
   // the computed does not trigger, when for example the camera position changes

+ 1 - 2
src/composables/useLoader/index.ts

@@ -1,6 +1,6 @@
 import type { Loader, LoadingManager, Object3D } from 'three'
 import type { TresObject } from '../../types'
-import { useLogger } from '..'
+import { logError } from '../../utils/logger'
 
 export interface TresLoader<T> extends Loader {
   load: (
@@ -72,7 +72,6 @@ export async function useLoader<T>(
   onProgress?: (event: ProgressEvent<EventTarget>) => void,
   cb?: (proto: TresLoader<T>) => void,
 ): Promise<T | T[]> {
-  const { logError } = useLogger()
   const proto = new Loader()
   if (cb) {
     cb(proto)

+ 1 - 3
src/composables/useRenderer/index.ts

@@ -18,7 +18,7 @@ import { revision } from '../../core/revision'
 import { get, merge, set, setPixelRatio } from '../../utils'
 
 import { normalizeColor } from '../../utils/normalize'
-import { useLogger } from '../useLogger'
+import { logError } from '../../utils/logger'
 import { rendererPresets } from './const'
 
 type TransformToMaybeRefOrGetter<T> = {
@@ -155,8 +155,6 @@ export function useRenderer(
 
   const { pixelRatio } = useDevicePixelRatio()
 
-  const { logError } = useLogger()
-
   const getThreeRendererDefaults = () => {
     const plainRenderer = new WebGLRenderer()
 

+ 1 - 3
src/composables/useSeek/index.ts

@@ -1,5 +1,5 @@
 import type { Object3D, Scene } from 'three'
-import { useLogger } from '../useLogger'
+import { logWarning } from '../../utils/logger'
 
 /**
  * Seek composable return type
@@ -21,8 +21,6 @@ export interface UseSeekReturn {
  * @return {*}  {UseSeekReturn}
  */
 export function useSeek(): UseSeekReturn {
-  const { logWarning } = useLogger()
-
   /**
    * Returns a child object of the parent given a property
    *

+ 5 - 5
src/composables/useTresEventManager/index.ts

@@ -3,8 +3,8 @@ import type { Object3D, Object3DEventMap, Scene } from 'three'
 import type { TresContext } from '../useTresContextProvider'
 import { shallowRef } from 'vue'
 import { hyphenate } from '../../utils'
-import * as is from '../../utils/is'
 import { useRaycaster } from '../useRaycaster'
+import { isObject3D, isTresObject } from '../../utils/is'
 
 export interface TresEventManager {
   /**
@@ -183,13 +183,13 @@ export function useTresEventManager(
   })
 
   function registerObject(maybeTresObject: unknown) {
-    if (is.tresObject(maybeTresObject) && is.object3D(maybeTresObject)) {
+    if (isTresObject(maybeTresObject) && isObject3D(maybeTresObject)) {
       objectsWithEvents.value.push(maybeTresObject as TresInstance)
     }
   }
 
   function deregisterObject(maybeTresObject: unknown) {
-    if (is.tresObject(maybeTresObject) && is.object3D(maybeTresObject)) {
+    if (isTresObject(maybeTresObject) && isObject3D(maybeTresObject)) {
       const index = objectsWithEvents.value.indexOf(maybeTresObject as TresInstance)
       if (index > -1) {
         objectsWithEvents.value.splice(index, 1)
@@ -198,13 +198,13 @@ export function useTresEventManager(
   }
 
   function registerPointerMissedObject(maybeTresObject: unknown) {
-    if (is.tresObject(maybeTresObject) && is.object3D(maybeTresObject) && maybeTresObject.onPointerMissed) {
+    if (isTresObject(maybeTresObject) && isObject3D(maybeTresObject) && maybeTresObject.onPointerMissed) {
       pointerMissedObjects.push(maybeTresObject)
     }
   }
 
   function deregisterPointerMissedObject(maybeTresObject: unknown) {
-    if (is.tresObject(maybeTresObject) && is.object3D(maybeTresObject)) {
+    if (isTresObject(maybeTresObject) && isObject3D(maybeTresObject)) {
       const index = pointerMissedObjects.indexOf(maybeTresObject)
       if (index > -1) {
         pointerMissedObjects.splice(index, 1)

+ 15 - 17
src/core/nodeOps.ts

@@ -2,14 +2,12 @@ import type { TresContext } from '../composables'
 import type { DisposeType, LocalState, TresInstance, TresObject, TresObject3D, TresPrimitive, WithMathProps } from '../types'
 import { BufferAttribute, Object3D } from 'three'
 import { isRef, type RendererOptions } from 'vue'
-import { useLogger } from '../composables'
 import { attach, deepArrayEqual, doRemoveDeregister, doRemoveDetach, invalidateInstance, isHTMLTag, kebabToCamel, noop, prepareTresInstance, setPrimitiveObject, unboxTresPrimitive } from '../utils'
-import * as is from '../utils/is'
+import { logError } from '../utils/logger'
+import { isArray, isFunction, isObject, isObject3D, isScene, isUndefined } from '../utils/is'
 import { createRetargetingProxy } from '../utils/primitive/createRetargetingProxy'
 import { catalogue } from './catalogue'
 
-const { logError } = useLogger()
-
 const supportedPointerEvents = [
   'onClick',
   'onContextMenu',
@@ -42,7 +40,7 @@ export const nodeOps: (context: TresContext) => RendererOptions<TresObject, Tres
     let obj: TresObject | null
 
     if (tag === 'primitive') {
-      if (!is.obj(props.object) || isRef(props.object)) {
+      if (!isObject(props.object) || isRef(props.object)) {
         logError(
           'Tres primitives need an \'object\' prop, whose value is an object or shallowRef<object>',
         )
@@ -123,7 +121,7 @@ export const nodeOps: (context: TresContext) => RendererOptions<TresObject, Tres
     if (childInstance.__tres.attach) {
       attach(parentInstance, childInstance, childInstance.__tres.attach)
     }
-    else if (is.object3D(child) && is.object3D(parentInstance)) {
+    else if (isObject3D(child) && isObject3D(parentInstance)) {
       parentInstance.add(child)
       child.dispatchEvent({ type: 'added' })
     }
@@ -155,9 +153,9 @@ export const nodeOps: (context: TresContext) => RendererOptions<TresObject, Tres
 
     // NOTE: Derive `dispose` value for this `remove` call and
     // recursive remove calls.
-    dispose = is.und(dispose) ? 'default' : dispose
+    dispose = isUndefined(dispose) ? 'default' : dispose
     const userDispose = node.__tres?.dispose
-    if (!is.und(userDispose)) {
+    if (!isUndefined(userDispose)) {
       if (userDispose === null) {
         // NOTE: Treat as `false` to act like R3F
         dispose = false
@@ -211,11 +209,11 @@ export const nodeOps: (context: TresContext) => RendererOptions<TresObject, Tres
     doRemoveDeregister(node, context)
 
     // NOTE: 4) Dispose `node`
-    if (shouldDispose && !is.scene(node)) {
-      if (is.fun(dispose)) {
+    if (shouldDispose && !isScene(node)) {
+      if (isFunction(dispose)) {
         dispose(node as TresInstance)
       }
-      else if (is.fun(node.dispose)) {
+      else if (isFunction(node.dispose)) {
         try {
           node.dispose()
         }
@@ -267,7 +265,7 @@ export const nodeOps: (context: TresContext) => RendererOptions<TresObject, Tres
       return
     }
 
-    if (is.object3D(node) && key === 'blocks-pointer-events') {
+    if (isObject3D(node) && key === 'blocks-pointer-events') {
       if (nextValue || nextValue === '') { node[key] = nextValue }
       else { delete node[key] }
       return
@@ -321,23 +319,23 @@ export const nodeOps: (context: TresContext) => RendererOptions<TresObject, Tres
     let value = nextValue
     if (value === '') { value = true }
     // Set prop, prefer atomic methods if applicable
-    if (is.fun(target)) {
+    if (isFunction(target)) {
       // don't call pointer event callback functions
 
       if (!supportedPointerEvents.includes(prop)) {
-        if (is.arr(value)) { node[finalKey](...value) }
+        if (isArray(value)) { node[finalKey](...value) }
         else { node[finalKey](value) }
       }
       // NOTE: Set on* callbacks
       // Issue: https://github.com/Tresjs/tres/issues/360
-      if (finalKey.startsWith('on') && is.fun(value)) {
+      if (finalKey.startsWith('on') && isFunction(value)) {
         root[finalKey] = value
       }
       return
     }
-    if (!target?.set && !is.fun(target)) { root[finalKey] = value }
+    if (!target?.set && !isFunction(target)) { root[finalKey] = value }
     else if (target.constructor === value.constructor && target?.copy) { target?.copy(value) }
-    else if (is.arr(value)) { target.set(...value) }
+    else if (isArray(value)) { target.set(...value) }
     else if (!target.isColor && target.setScalar) { target.setScalar(value) }
     else { target.set(value) }
 

+ 3 - 2
src/devtools/plugin.ts

@@ -9,10 +9,11 @@ import {
 import { Color, type Mesh } from 'three'
 import { reactive } from 'vue'
 import { createHighlightMesh, editSceneObject } from '../utils'
-import * as is from '../utils/is'
 import { bytesToKB, calculateMemoryUsage } from '../utils/perf'
 import { toastMessage } from './utils'
 
+import { isLight } from '../utils/is'
+
 export interface Tags {
   label: string
   textColor: number
@@ -52,7 +53,7 @@ const createNode = (object: TresObject): SceneGraphObject => {
   }
 
   if (object.type.includes('Light')) {
-    if (is.light(object)) {
+    if (isLight(object)) {
       node.tags.push({
         label: `${object.intensity}`,
         textColor: 0x9499A6,

+ 1 - 3
src/directives/vDistanceTo.ts

@@ -1,11 +1,9 @@
 import type { Ref } from 'vue'
 import type { TresObject } from '../types'
 import { ArrowHelper } from 'three'
-import { useLogger } from '../composables'
+import { logWarning } from '../utils/logger'
 import { extractBindingPosition } from '../utils'
 
-const { logWarning } = useLogger()
-
 let arrowHelper: ArrowHelper | null = null
 
 export const vDistanceTo = {

+ 1 - 3
src/directives/vLightHelper.ts

@@ -9,9 +9,7 @@ import {
   SpotLightHelper,
 } from 'three'
 import { RectAreaLightHelper } from 'three-stdlib'
-import { useLogger } from '../composables'
-
-const { logWarning } = useLogger()
+import { logWarning } from '../utils/logger'
 
 type LightHelper = typeof DirectionalLightHelper
   | typeof PointLightHelper

+ 1 - 0
src/index.ts

@@ -10,6 +10,7 @@ export * from './core/catalogue'
 export * from './core/loop'
 export * from './directives'
 export * from './types'
+export * from './utils/logger'
 
 export interface TresOptions {
   extends?: Record<string, unknown>

+ 12 - 15
src/utils/index.ts

@@ -4,8 +4,9 @@ import type { Material, Mesh, Object3D, Texture } from 'three'
 import type { TresContext } from '../composables/useTresContextProvider'
 import { DoubleSide, MathUtils, MeshBasicMaterial, Scene, Vector3 } from 'three'
 import { HightlightMesh } from '../devtools/highlight'
-import * as is from './is'
+import { isFunction, isNumber, isString, isTresPrimitive, isUndefined } from './is'
 
+export * from './logger'
 export function toSetMethodName(key: string) {
   return `set${key[0].toUpperCase()}${key.slice(1)}`
 }
@@ -162,11 +163,6 @@ export function deepArrayEqual(arr1: any[], arr2: any[]): boolean {
   return true
 }
 
-/**
- * TypeSafe version of Array.isArray
- */
-export const isArray = Array.isArray as (a: any) => a is any[] | readonly any[]
-
 export function editSceneObject(scene: Scene, objectUuid: string, propertyPath: string[], value: any): void {
   // Function to recursively find the object by UUID
   const findObjectByUuid = (node: Object3D): Object3D | undefined => {
@@ -360,7 +356,7 @@ function joinAsCamelCase(...strings: string[]): string {
 const INDEX_REGEX = /-\d+$/
 
 export function attach(parent: TresInstance, child: TresInstance, type: AttachType) {
-  if (is.str(type)) {
+  if (isString(type)) {
     // NOTE: If attaching into an array (foo-0), create one
     if (INDEX_REGEX.test(type)) {
       const typeWithoutTrailingIndex = type.replace(INDEX_REGEX, '')
@@ -373,7 +369,7 @@ export function attach(parent: TresInstance, child: TresInstance, type: AttachTy
         const previousAttach = target[key]
         const augmentedArray: any[] & { __tresDetach?: () => void } = []
         augmentedArray.__tresDetach = () => {
-          if (augmentedArray.every(v => is.und(v))) {
+          if (augmentedArray.every(v => isUndefined(v))) {
             target[key] = previousAttach
           }
         }
@@ -391,7 +387,7 @@ export function attach(parent: TresInstance, child: TresInstance, type: AttachTy
 }
 
 export function detach(parent: any, child: TresInstance, type: AttachType) {
-  if (is.str(type)) {
+  if (isString(type)) {
     const { target, key } = resolve(parent, type)
     const previous = child.__tres.previousAttach
     // When the previous value was undefined, it means the value was never set to begin with
@@ -455,15 +451,15 @@ export function noop(fn: string): any {
 export function setPixelRatio(renderer: { setPixelRatio?: (dpr: number) => void, getPixelRatio?: () => number }, systemDpr: number, userDpr?: number | [number, number]) {
   // NOTE: Optional `setPixelRatio` allows this function to accept
   // THREE renderers like SVGRenderer.
-  if (!is.fun(renderer.setPixelRatio)) { return }
+  if (!isFunction(renderer.setPixelRatio)) { return }
 
   let newDpr = 0
 
-  if (userDpr && is.arr(userDpr) && userDpr.length >= 2) {
+  if (userDpr && Array.isArray(userDpr) && userDpr.length >= 2) {
     const [min, max] = userDpr
     newDpr = MathUtils.clamp(systemDpr, min, max)
   }
-  else if (is.num(userDpr)) { newDpr = userDpr }
+  else if (isNumber(userDpr)) { newDpr = userDpr }
   else { newDpr = systemDpr }
 
   // NOTE: Don't call `setPixelRatio` unless both:
@@ -524,7 +520,7 @@ export function setPrimitiveObject(
 }
 
 export function unboxTresPrimitive<T>(maybePrimitive: T): T | TresInstance {
-  if (is.tresPrimitive(maybePrimitive)) {
+  if (isTresPrimitive(maybePrimitive)) {
     // NOTE:
     // `primitive` has-a THREE object. Multiple `primitive`s can have
     // the same THREE object. We want to allow the same THREE object
@@ -532,8 +528,9 @@ export function unboxTresPrimitive<T>(maybePrimitive: T): T | TresInstance {
     // that, e.g., materials and geometries.
     // But __tres (`LocalState`) only allows for a single parent.
     // So: copy `__tres` to the object when unboxing.
-    maybePrimitive.object.__tres = maybePrimitive.__tres
-    return maybePrimitive.object
+    const primitive = maybePrimitive as unknown as TresPrimitive
+    primitive.object.__tres = primitive.__tres
+    return primitive.object
   }
   else {
     return maybePrimitive

+ 16 - 17
src/utils/is.test.ts

@@ -1,6 +1,6 @@
 import type { Camera, Light, Material, Object3D } from 'three'
 import { AmbientLight, BufferGeometry, DirectionalLight, Fog, Group, Mesh, MeshBasicMaterial, MeshNormalMaterial, OrthographicCamera, PerspectiveCamera, PointLight, Scene } from 'three'
-import * as is from './is'
+import { isArray, isBoolean, isBufferGeometry, isCamera, isFog, isFunction, isLight, isMaterial, isNumber, isObject, isObject3D, isScene, isString, isTresObject, isUndefined } from './is'
 
 const NUMBERS: Record<string, number> = {
   '0': 0,
@@ -89,27 +89,26 @@ const BUFFER_GEOMETRIES: Record<string, BufferGeometry> = {
 
 const OBJECTS = Object.assign({}, { '{}': {}, '{ a: "a" }': { a: 'a' } }, FOGS, MATERIALS, OBJECT3DS, BUFFER_GEOMETRIES)
 
-// NOTE: See definition in is.ts
 const TRES_OBJECTS = Object.assign({}, MATERIALS, OBJECT3DS, BUFFER_GEOMETRIES, FOGS)
 
 const ALL = Object.assign({}, NUMBERS, BOOLEANS, STRINGS, NULL, UNDEFINED, ARRAYS, FUNCTIONS, OBJECTS)
 
 describe('is', () => {
-  describe('is.und(a: any)', () => { test(is.und, UNDEFINED) })
-  describe('is.arr(a: any)', () => { test(is.arr, ARRAYS) })
-  describe('is.num(a: any)', () => { test(is.num, NUMBERS) })
-  describe('is.str(a: any)', () => { test(is.str, STRINGS) })
-  describe('is.bool(a: any)', () => { test(is.bool, BOOLEANS) })
-  describe('is.fun(a: any)', () => { test(is.fun, FUNCTIONS) })
-  describe('is.obj(a: any)', () => { test(is.obj, OBJECTS) })
-  describe('is.object3D(a: any)', () => { test(is.object3D, OBJECT3DS) })
-  describe('is.camera(a: any)', () => { test(is.camera, CAMERAS) })
-  describe('is.bufferGeometry(a: any)', () => { test(is.bufferGeometry, BUFFER_GEOMETRIES) })
-  describe('is.material(a: any)', () => { test(is.material, MATERIALS) })
-  describe('is.light(a: any)', () => { test(is.light, LIGHTS) })
-  describe('is.fog(a: any)', () => { test(is.fog, FOGS) })
-  describe('is.scene(a: any)', () => { test(is.scene, SCENES) })
-  describe('is.tresObject(a: any)', () => { test(is.tresObject, TRES_OBJECTS) })
+  describe('isUndefined(a: any)', () => { test(isUndefined, UNDEFINED) })
+  describe('isArray(a: any)', () => { test(isArray, ARRAYS) })
+  describe('isNumber(a: any)', () => { test(isNumber, NUMBERS) })
+  describe('isString(a: any)', () => { test(isString, STRINGS) })
+  describe('isBoolean(a: any)', () => { test(isBoolean, BOOLEANS) })
+  describe('isFunction(a: any)', () => { test(isFunction, FUNCTIONS) })
+  describe('isObject(a: any)', () => { test(isObject, OBJECTS) })
+  describe('isObject3D(a: any)', () => { test(isObject3D, OBJECT3DS) })
+  describe('isCamera(a: any)', () => { test(isCamera, CAMERAS) })
+  describe('isBufferGeometry(a: any)', () => { test(isBufferGeometry, BUFFER_GEOMETRIES) })
+  describe('isMaterial(a: any)', () => { test(isMaterial, MATERIALS) })
+  describe('isLight(a: any)', () => { test(isLight, LIGHTS) })
+  describe('isFog(a: any)', () => { test(isFog, FOGS) })
+  describe('isScene(a: any)', () => { test(isScene, SCENES) })
+  describe('isTresObject(a: any)', () => { test(isTresObject, TRES_OBJECTS) })
 })
 
 /**

+ 265 - 32
src/utils/is.ts

@@ -1,68 +1,301 @@
 import type { TresObject, TresPrimitive } from 'src/types'
 import type { BufferGeometry, Camera, Fog, Light, Material, Object3D, Scene } from 'three'
 
-export function und(u: unknown): u is undefined {
-  return typeof u === 'undefined'
+/**
+ * Type guard to check if a value is undefined
+ * @param value - The value to check
+ * @returns True if the value is undefined, false otherwise
+ * @example
+ * ```ts
+ * const value = undefined
+ * if (isUndefined(value)) {
+ *   // TypeScript knows value is undefined here
+ * }
+ * ```
+ */
+export function isUndefined(value: unknown): value is undefined {
+  return typeof value === 'undefined'
 }
 
-export function arr(u: unknown): u is Array<unknown> {
-  return Array.isArray(u)
+/**
+ * Type guard to check if a value is an array
+ * @param value - The value to check
+ * @returns True if the value is an array, false otherwise
+ * @example
+ * ```ts
+ * const value = [1, 2, 3]
+ * if (isArray(value)) {
+ *   // TypeScript knows value is Array<unknown> here
+ *   value.length // OK
+ *   value.map(x => x) // OK
+ * }
+ * ```
+ */
+export function isArray(value: unknown): value is Array<unknown> {
+  return Array.isArray(value)
 }
 
-export function num(u: unknown): u is number {
-  return typeof u === 'number'
+/**
+ * Type guard to check if a value is a number
+ * @param value - The value to check
+ * @returns True if the value is a number (including NaN and Infinity), false otherwise
+ * @example
+ * ```ts
+ * const value = 42
+ * if (isNumber(value)) {
+ *   // TypeScript knows value is number here
+ *   value.toFixed(2) // OK
+ *   value * 2 // OK
+ * }
+ * ```
+ */
+export function isNumber(value: unknown): value is number {
+  return typeof value === 'number'
 }
 
-export function str(u: unknown): u is string {
-  return typeof u === 'string'
+/**
+ * Type guard to check if a value is a string
+ * @param value - The value to check
+ * @returns True if the value is a string, false otherwise
+ * @example
+ * ```ts
+ * const value = "hello"
+ * if (isString(value)) {
+ *   // TypeScript knows value is string here
+ *   value.length // OK
+ *   value.toUpperCase() // OK
+ * }
+ * ```
+ */
+export function isString(value: unknown): value is string {
+  return typeof value === 'string'
 }
 
-export function bool(u: unknown): u is boolean {
-  return u === true || u === false
+/**
+ * Type guard to check if a value is a boolean
+ * @param value - The value to check
+ * @returns True if the value is strictly true or false, false otherwise
+ * @example
+ * ```ts
+ * const value = true
+ * if (isBoolean(value)) {
+ *   // TypeScript knows value is boolean here
+ *   !value // OK
+ *   value && true // OK
+ * }
+ * ```
+ */
+export function isBoolean(value: unknown): value is boolean {
+  return value === true || value === false
 }
 
-export function fun(u: unknown): u is (...args: any[]) => any {
-  return typeof u === 'function'
+/**
+ * Type guard to check if a value is a function
+ * @param value - The value to check
+ * @returns True if the value is a callable function, false otherwise
+ * @example
+ * ```ts
+ * const value = () => {}
+ * if (isFunction(value)) {
+ *   // TypeScript knows value is (...args: any[]) => any here
+ *   value() // OK
+ *   value.call(null) // OK
+ * }
+ * ```
+ */
+export function isFunction(value: unknown): value is (...args: any[]) => any {
+  return typeof value === 'function'
 }
 
-export function obj(u: unknown): u is Record<string | number | symbol, unknown> {
-  return u === Object(u) && !arr(u) && !fun(u)
+/**
+ * Type guard to check if a value is a plain object
+ * @param value - The value to check
+ * @returns True if the value is a plain object (not null, array, or function), false otherwise
+ * @example
+ * ```ts
+ * const value = { key: 'value' }
+ * if (isObject(value)) {
+ *   // TypeScript knows value is Record<string | number | symbol, unknown> here
+ *   Object.keys(value) // OK
+ *   value.key // OK
+ * }
+ * ```
+ */
+export function isObject(value: unknown): value is Record<string | number | symbol, unknown> {
+  return value === Object(value) && !isArray(value) && !isFunction(value)
 }
 
-export function object3D(u: unknown): u is Object3D {
-  return obj(u) && !!(u.isObject3D)
+/**
+ * Type guard to check if a value is a Three.js Object3D
+ * @param value - The value to check
+ * @returns True if the value is a Three.js Object3D instance, false otherwise
+ * @example
+ * ```ts
+ * const value = new THREE.Object3D()
+ * if (isObject3D(value)) {
+ *   // TypeScript knows value is Object3D here
+ *   value.position // OK
+ *   value.rotation // OK
+ *   value.scale // OK
+ * }
+ * ```
+ */
+export function isObject3D(value: unknown): value is Object3D {
+  return isObject(value) && !!(value.isObject3D)
 }
 
-export function camera(u: unknown): u is Camera {
-  return obj(u) && !!(u.isCamera)
+/**
+ * Type guard to check if a value is a Three.js Camera
+ * @param value - The value to check
+ * @returns True if the value is a Three.js Camera instance, false otherwise
+ * @example
+ * ```ts
+ * const value = new THREE.PerspectiveCamera()
+ * if (isCamera(value)) {
+ *   // TypeScript knows value is Camera here
+ *   value.fov // OK
+ *   value.near // OK
+ *   value.far // OK
+ * }
+ * ```
+ */
+export function isCamera(value: unknown): value is Camera {
+  return isObject(value) && !!(value.isCamera)
 }
 
-export function bufferGeometry(u: unknown): u is BufferGeometry {
-  return obj(u) && !!(u.isBufferGeometry)
+/**
+ * Type guard to check if a value is a Three.js BufferGeometry
+ * @param value - The value to check
+ * @returns True if the value is a Three.js BufferGeometry instance, false otherwise
+ * @example
+ * ```ts
+ * const value = new THREE.BufferGeometry()
+ * if (isBufferGeometry(value)) {
+ *   // TypeScript knows value is BufferGeometry here
+ *   value.attributes // OK
+ *   value.index // OK
+ *   value.computeVertexNormals() // OK
+ * }
+ * ```
+ */
+export function isBufferGeometry(value: unknown): value is BufferGeometry {
+  return isObject(value) && !!(value.isBufferGeometry)
 }
 
-export function material(u: unknown): u is Material {
-  return obj(u) && !!(u.isMaterial)
+/**
+ * Type guard to check if a value is a Three.js Material
+ * @param value - The value to check
+ * @returns True if the value is a Three.js Material instance, false otherwise
+ * @example
+ * ```ts
+ * const value = new THREE.MeshStandardMaterial()
+ * if (isMaterial(value)) {
+ *   // TypeScript knows value is Material here
+ *   value.color // OK
+ *   value.metalness // OK
+ *   value.roughness // OK
+ * }
+ * ```
+ */
+export function isMaterial(value: unknown): value is Material {
+  return isObject(value) && !!(value.isMaterial)
 }
 
-export function light(u: unknown): u is Light {
-  return obj(u) && !!(u.isLight)
+/**
+ * Type guard to check if a value is a Three.js Light
+ * @param value - The value to check
+ * @returns True if the value is a Three.js Light instance, false otherwise
+ * @example
+ * ```ts
+ * const value = new THREE.DirectionalLight()
+ * if (isLight(value)) {
+ *   // TypeScript knows value is Light here
+ *   value.intensity // OK
+ *   value.color // OK
+ *   value.position // OK
+ * }
+ * ```
+ */
+export function isLight(value: unknown): value is Light {
+  return isObject(value) && !!(value.isLight)
 }
 
-export function fog(u: unknown): u is Fog {
-  return obj(u) && !!(u.isFog)
+/**
+ * Type guard to check if a value is a Three.js Fog
+ * @param value - The value to check
+ * @returns True if the value is a Three.js Fog instance, false otherwise
+ * @example
+ * ```ts
+ * const value = new THREE.Fog(0x000000, 1, 1000)
+ * if (isFog(value)) {
+ *   // TypeScript knows value is Fog here
+ *   value.color // OK
+ *   value.near // OK
+ *   value.far // OK
+ * }
+ * ```
+ */
+export function isFog(value: unknown): value is Fog {
+  return isObject(value) && !!(value.isFog)
 }
 
-export function scene(u: unknown): u is Scene {
-  return obj(u) && !!(u.isScene)
+/**
+ * Type guard to check if a value is a Three.js Scene
+ * @param value - The value to check
+ * @returns True if the value is a Three.js Scene instance, false otherwise
+ * @example
+ * ```ts
+ * const value = new THREE.Scene()
+ * if (isScene(value)) {
+ *   // TypeScript knows value is Scene here
+ *   value.children // OK
+ *   value.add(new THREE.Object3D()) // OK
+ *   value.remove(new THREE.Object3D()) // OK
+ * }
+ * ```
+ */
+export function isScene(value: unknown): value is Scene {
+  return isObject(value) && !!(value.isScene)
 }
 
-export function tresObject(u: unknown): u is TresObject {
+/**
+ * Type guard to check if a value is a TresObject
+ * @param value - The value to check
+ * @returns True if the value is a TresObject (Object3D | BufferGeometry | Material | Fog), false otherwise
+ * @example
+ * ```ts
+ * const value = new THREE.Mesh()
+ * if (isTresObject(value)) {
+ *   // TypeScript knows value is TresObject here
+ *   // You can use common properties and methods shared by all TresObjects
+ * }
+ * ```
+ * @remarks
+ * TresObject is a union type that represents the core Three.js objects that can be used in TresJS.
+ * This includes Object3D, BufferGeometry, Material, and Fog instances.
+ */
+export function isTresObject(value: unknown): value is TresObject {
   // NOTE: TresObject is currently defined as
   // TresObject3D | THREE.BufferGeometry | THREE.Material | THREE.Fog
-  return object3D(u) || bufferGeometry(u) || material(u) || fog(u)
+  return isObject3D(value) || isBufferGeometry(value) || isMaterial(value) || isFog(value)
 }
 
-export function tresPrimitive(u: unknown): u is TresPrimitive {
-  return obj(u) && !!(u.isPrimitive)
+/**
+ * Type guard to check if a value is a TresPrimitive
+ * @param value - The value to check
+ * @returns True if the value is a TresPrimitive instance, false otherwise
+ * @example
+ * ```ts
+ * const value = { isPrimitive: true }
+ * if (isTresPrimitive(value)) {
+ *   // TypeScript knows value is TresPrimitive here
+ *   // You can use properties and methods specific to TresPrimitives
+ * }
+ * ```
+ * @remarks
+ * TresPrimitive is a special type in TresJS that represents primitive objects
+ * that can be used directly in the scene without needing to be wrapped in a Three.js object.
+ */
+export function isTresPrimitive(value: unknown): value is TresPrimitive {
+  return isObject(value) && !!(value.isPrimitive)
 }

+ 21 - 16
src/composables/useLogger.ts → src/utils/logger.ts

@@ -1,3 +1,8 @@
+/**
+ * Logger utility for TresJS
+ * @module logger
+ */
+
 /* eslint-disable no-console */
 export const isProd = import.meta.env.MODE === 'production'
 
@@ -5,13 +10,11 @@ const logPrefix = '[TresJS ▲ ■ ●] '
 
 type OneOrMore<T> = { 0: T } & Array<T>
 
-interface LoggerComposition {
-  logError: (...args: OneOrMore<any>) => void
-  logWarning: (...args: OneOrMore<any>) => void
-  logMessage: (name: string, value: any) => void
-}
-
-function logError(...args: OneOrMore<any>) {
+/**
+ * Logs an error message with the TresJS prefix
+ * @param args - Arguments to log
+ */
+export function logError(...args: OneOrMore<any>): void {
   if (typeof args[0] === 'string') {
     // NOTE: Don't break console string substitution
     args[0] = logPrefix + args[0]
@@ -22,7 +25,11 @@ function logError(...args: OneOrMore<any>) {
   console.error(...args)
 }
 
-function logWarning(...args: OneOrMore<any>) {
+/**
+ * Logs a warning message with the TresJS prefix
+ * @param args - Arguments to log
+ */
+export function logWarning(...args: OneOrMore<any>): void {
   if (typeof args[0] === 'string') {
     // NOTE: Don't break console string substitution
     args[0] = logPrefix + args[0]
@@ -33,16 +40,14 @@ function logWarning(...args: OneOrMore<any>) {
   console.warn(...args)
 }
 
-function logMessage(name: string, value: any) {
+/**
+ * Logs a message with the TresJS prefix (only in development mode)
+ * @param name - Name of the message
+ * @param value - Value to log
+ */
+export function logMessage(name: string, value: any): void {
   if (!isProd) {
     console.log(`${logPrefix} - ${name}:`, value)
   }
 }
 /* eslint-enable no-console */
-export function useLogger(): LoggerComposition {
-  return {
-    logError,
-    logWarning,
-    logMessage,
-  }
-}

+ 2 - 7
vite.config.ts

@@ -46,10 +46,12 @@ export default defineConfig({
     threads: false,
   },
   build: {
+    // vite.config.ts
     lib: {
       entry: resolve(__dirname, 'src/index.ts'),
       name: 'tres',
       fileName: 'tres',
+      formats: ['es'],
     },
     watch: {
       include: [resolve(__dirname, 'src')],
@@ -71,13 +73,6 @@ export default defineConfig({
       external: ['vue', '@vueuse/core', 'three'],
       output: {
         exports: 'named',
-        // Provide global variables to use in the UMD build
-        // for externalized deps
-        globals: {
-          'vue': 'Vue',
-          '@vueuse/core': 'VueUseCore',
-          'three': 'Three',
-        },
       },
     },
   },