diff --git a/.github/workflows/pkg-pr-new.yml b/.github/workflows/pkg-pr-new.yml
index fde6babb..2ed6d3ef 100644
--- a/.github/workflows/pkg-pr-new.yml
+++ b/.github/workflows/pkg-pr-new.yml
@@ -26,6 +26,12 @@ jobs:
- name: Install dependencies
run: pnpm install
+ - name: Typecheck
+ run: pnpm lint:types
+
+ - name: Test
+ run: pnpm test --run
+
- name: Build
run: pnpm build
diff --git a/playground/App.tsx b/playground/App.tsx
index b2b6392b..f26e6d3a 100644
--- a/playground/App.tsx
+++ b/playground/App.tsx
@@ -1,3 +1,4 @@
+///
import { A, Route, Router } from "@solidjs/router"
import { createSignal, For, lazy, type ParentProps } from "solid-js"
import * as THREE from "three"
@@ -171,6 +172,5 @@ export function App() {
/>
)
- console.log(router.toArray())
return router
}
diff --git a/playground/controls/process-props.ts b/playground/controls/process-props.ts
index 77ad58df..13e6b3bf 100644
--- a/playground/controls/process-props.ts
+++ b/playground/controls/process-props.ts
@@ -1,11 +1,11 @@
-import { splitProps } from "solid-js"
-import { defaultProps } from "./default-props.ts"
+import { mergeProps, type MergeProps, splitProps } from "solid-js"
import type { KeyOfOptionals } from "./type-utils.ts"
export function processProps<
const TProps,
- const TDefaults extends Required>>,
- const TSplit extends readonly (keyof TProps)[],
+ const TDefaults extends Partial>>,
+ const TSplit extends readonly (keyof MergeProps<[TDefaults, TProps]>)[],
>(props: TProps, defaults: TDefaults, split?: TSplit) {
- return splitProps(defaultProps(props, defaults), split ?? [])
+ const merged = mergeProps(defaults, props)
+ return splitProps(merged, (split ?? []) as readonly (keyof typeof merged)[])
}
diff --git a/playground/src/api/canvas/usage.tsx b/playground/src/api/canvas/usage.tsx
index 3338c641..ebebcfec 100644
--- a/playground/src/api/canvas/usage.tsx
+++ b/playground/src/api/canvas/usage.tsx
@@ -63,19 +63,20 @@ export default function () {
fov: 75,
}}
fallback={Loading Canvas...
}
- gl={{
- antialias: true,
- alpha: true,
- powerPreference: "high-performance",
- }}
+ gl={canvas =>
+ new THREE.WebGLRenderer({ canvas, antialias: true, alpha: true, powerPreference: "high-performance" })
+ }
scene={{
background: new THREE.Color(0x202020),
fog: new THREE.Fog(0x202020, 10, 50),
}}
defaultRaycaster={{
params: {
+ Mesh: {},
Line: { threshold: 0.1 },
+ LOD: {},
Points: { threshold: 0.1 },
+ Sprite: {},
},
}}
shadows={shadows()}
diff --git a/playground/src/api/use-loader/single-texture.tsx b/playground/src/api/use-loader/single-texture.tsx
index 9cb8d7fa..3355bba0 100644
--- a/playground/src/api/use-loader/single-texture.tsx
+++ b/playground/src/api/use-loader/single-texture.tsx
@@ -15,15 +15,7 @@ function SkyboxSphere() {
"https://threejs.org/examples/textures/cube/SwedishRoyalCastle/ny.jpg", // negative y
"https://threejs.org/examples/textures/cube/SwedishRoyalCastle/pz.jpg", // positive z
"https://threejs.org/examples/textures/cube/SwedishRoyalCastle/nz.jpg", // negative z
- ],
- // CubeTextureLoader properties
- {
- mapping: THREE.CubeReflectionMapping,
- wrapS: THREE.ClampToEdgeWrapping,
- wrapT: THREE.ClampToEdgeWrapping,
- magFilter: THREE.LinearFilter,
- minFilter: THREE.LinearMipmapLinearFilter,
- },
+ ] as string[],
)
return (
diff --git a/src/components.tsx b/src/components.tsx
index 858ce837..9038a847 100644
--- a/src/components.tsx
+++ b/src/components.tsx
@@ -1,7 +1,5 @@
-import { whenMemo } from "@bigmistqke/solid-whenever"
import {
Show,
- createEffect,
createMemo,
mergeProps,
splitProps,
@@ -94,22 +92,18 @@ type EntityProps> = Overwrite<
*/
export function Entity>(props: EntityProps) {
const [config, rest] = splitProps(props, ["from", "args"])
- const memo = whenMemo(
- () => config.from,
- from => {
- // listen to key changes
- props.key
- const instance = meta(
- isConstructor(from) ? autodispose(new from(...(config.args ?? []))) : from,
- {
- props,
- },
- ) as Meta
- useProps(instance, rest)
- return instance
- },
- )
- return memo as unknown as JSX.Element
+ const instance = createMemo(() => {
+ const from = config.from
+ if (!from) return undefined
+ // track key changes to force reconstruction
+ props.key
+ return meta(
+ isConstructor(from) ? autodispose(new from(...(config.args ?? []))) : from,
+ { props },
+ ) as Meta
+ })
+ useProps(instance, rest)
+ return instance as unknown as JSX.Element
}
/**********************************************************************************/
@@ -194,12 +188,10 @@ export function Resource>(props: Resou
options,
)
- createEffect(() => console.log("resource", resource()))
-
useProps(resource, rest)
return (
-
+
{resource => props.children?.(resource)}
)
diff --git a/src/create-events.ts b/src/create-events.ts
index f454747f..720e2279 100644
--- a/src/create-events.ts
+++ b/src/create-events.ts
@@ -59,7 +59,7 @@ export const isEventType = (type: string): type is EventName =>
function createThreeEvent<
TEvent extends Event,
TConfig extends { stoppable?: boolean; intersections?: Array },
->(nativeEvent: TEvent, { stoppable = true, intersections }: TConfig = {}) {
+>(nativeEvent: TEvent, { stoppable = true, intersections }: TConfig = {} as TConfig) {
const event: Record = stoppable
? {
nativeEvent,
@@ -128,7 +128,7 @@ function raycast(
stack.push(...object.children)
}
- return context.raycaster.intersectObjects(nodeSet.values().toArray(), false)
+ return context.raycaster.intersectObjects(Array.from(nodeSet), false)
}
/**********************************************************************************/
@@ -323,10 +323,11 @@ function createHoverEventRegistry(type: "Mouse" | "Pointer", context: Context) {
// Handle leave-event
const leaveEvent = createThreeEvent(nativeEvent, { intersections, stoppable: false })
- const leaveSet = hoveredSet.difference(enterSet)
+ const prevHoveredSet = hoveredSet
hoveredSet = enterSet
- for (const object of leaveSet.values()) {
+ for (const object of prevHoveredSet) {
+ if (enterSet.has(object)) continue
getMeta(object)?.props[`on${type}Leave`]?.(
// @ts-expect-error TODO: fix type-error
leaveEvent,
@@ -388,7 +389,7 @@ function createDefaultEventRegistry(
let node: Object3D | null = intersection.object
while (node && !event.stopped) {
- getMeta(intersection.object)?.props[type]?.(
+ getMeta(node)?.props[type]?.(
// @ts-expect-error TODO: fix type-error
event,
)
diff --git a/src/data-structure/stack.ts b/src/data-structure/stack.ts
index 6a7801fc..b4fba2ff 100644
--- a/src/data-structure/stack.ts
+++ b/src/data-structure/stack.ts
@@ -38,8 +38,7 @@ export class Stack {
array.push(value)
return array
})
- // @ts-expect-error TODO: fix type-error
- if (import.meta.env?.MODE === "development") {
+ if (process.env.NODE_ENV === "development") {
const array = untrack(this.#array.bind(this))
if (array.length > 2) {
// TODO: write better warning message
diff --git a/src/hooks.ts b/src/hooks.ts
index 2d9c07ec..4e9f4599 100644
--- a/src/hooks.ts
+++ b/src/hooks.ts
@@ -216,7 +216,7 @@ export function useLoader<
input: TInput,
): PromiseMaybe> {
if (isRecord(input)) {
- return awaitMapObject(input, value => getOrInsert(registry, loader, value)) as Promise<
+ return awaitMapObject(input, async value => getOrInsert(registry, loader, value)) as PromiseMaybe<
LoadOutput
>
} else {
@@ -225,11 +225,11 @@ export function useLoader<
const cachedPromise = registry.get(loader, _input, false)
if (cachedPromise) {
- return cachedPromise
+ return cachedPromise as PromiseMaybe>
}
- const promise = load(loader, input)
- registry.set(loader, input, promise)
+ const promise = load(loader, _input)
+ registry.set(loader, _input, promise)
return promise as Promise>
}
@@ -237,7 +237,7 @@ export function useLoader<
function loadUrl>(
url: TInput,
- ): Promise> {
+ ): PromiseMaybe> {
if (config.cache === true) {
if (!useLoader.cache) {
return load(loader(), url)
diff --git a/src/props.ts b/src/props.ts
index a2ba4d79..f23a28b0 100644
--- a/src/props.ts
+++ b/src/props.ts
@@ -32,35 +32,30 @@ function isWritable(object: object, propertyName: string) {
function applySceneGraph(parent: object, child: object) {
const parentMeta = getMeta(parent)
if (parentMeta) {
- // Update parent's augmented children-property.
parentMeta.children.add(child)
onCleanup(() => parentMeta.children.delete(child))
}
const childMeta = getMeta(child)
if (childMeta) {
- // Update parent's augmented children-property.
childMeta.parent = parent
onCleanup(() => (childMeta.parent = undefined))
}
let attachProp = childMeta?.props.attach
- // Attach-prop can be a callback. It returns a cleanup-function.
if (typeof attachProp === "function") {
- const cleanup = attachProp(parent, child as Meta)
+ const cleanup = attachProp(parent, child as Meta