UNPKG

@needle-tools/engine

Version:

Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.

108 lines (94 loc) 3.85 kB
import { type Constructor } from "./engine_types.js"; export declare type TypeResolver<T> = (data) => Constructor<T> | null; /** Please use {@link serializable} - this version has a typo and will be removed in future versions */ export const serializeable = function <T>(type?: Constructor<T> | null | Array<Constructor<any> | TypeResolver<T>> | TypeResolver<T>) { return serializable(type) } /** * Marks a field for serialization and editor exposure. Required for fields that reference * other objects, components, or assets. Primitive types (string, number, boolean) work without a type argument. * * @param type The constructor type for complex objects. Omit for primitives. * * @example Primitive types (no type needed) * ```ts * @serializable() * speed: number = 1; * * @serializable() * label: string = "Hello"; * ``` * @example Object references * ```ts * @serializable(Object3D) * target: Object3D | null = null; * * @serializable(Renderer) * myRenderer: Renderer | null = null; * ``` * @example Arrays * ```ts * @serializable([Object3D]) * waypoints: Object3D[] = []; * ``` * @see {@link syncField} for automatic network synchronization * @link https://engine.needle.tools/docs/reference/typescript-decorators.html#serializable */ export const serializable = function <T>(type?: Constructor<T> | null | Array<Constructor<any> | TypeResolver<T>> | TypeResolver<T>) { if (type === undefined) type = null; // for primitive types the serialization handles it without the constructor and just assigns the value // if the user still passes in a primitive type constructor we can just throw it away :) if (!Array.isArray(type)) { type = setNullForPrimitiveTypes(type); } else { for (let i = 0; i < type.length; i++) { const entry = type[i]; type[i] = setNullForPrimitiveTypes(entry); } } return function (_target: any, _propertyKey: string | { name: string }) { if (!_target) { const propertyName = typeof _propertyKey === 'string' ? _propertyKey : _propertyKey.name; console.warn(`@serializable without a target at '${propertyName}'.`); return; } // The _propertyKey parameter is a string in TS4 with experimentalDecorators // but a ClassFieldDecoratorContext in TS5 without. // Seems when a different TS version is used in VSCode for editor checking, we get errors here // if we don't also check for any. See https://github.com/needle-tools/needle-engine-support/issues/179 if (typeof _propertyKey !== 'string') { _propertyKey = _propertyKey.name; } // this is important so objects with inheritance dont override their serialized type // info if e.g. multiple classes inheriting from the same type implement a member with the same name // and both use @serializable() with different types if (!Object.getOwnPropertyDescriptor(_target, '$serializedTypes')) _target["$serializedTypes"] = {}; const types = _target["$serializedTypes"] = _target["$serializedTypes"] || {} types[_propertyKey] = type; } } function setNullForPrimitiveTypes(type) { switch (type?.prototype?.constructor?.name) { case "Number": case "String": case "Boolean": return null; } return type; } /** @internal */ export const ALL_PROPERTIES_MARKER = "__NEEDLE__ALL_PROPERTIES"; /** @internal @deprecated current not used */ export function allProperties(constructor: Function) { constructor[ALL_PROPERTIES_MARKER] = true; } /** * @internal */ export const STRICT_MARKER = "__NEEDLE__STRICT"; /** @deprecated current not used */ export function strict(constructor: Function) { constructor[STRICT_MARKER] = true; }