diff --git a/docs/src/content/docs/utilities/singleton-proxy.md b/docs/src/content/docs/utilities/singleton-proxy.md new file mode 100644 index 00000000..1df40beb --- /dev/null +++ b/docs/src/content/docs/utilities/singleton-proxy.md @@ -0,0 +1,22 @@ +--- +title: createSingletonProxy +description: ngxtension/singleton-proxy +--- + +`createSingletonProxy` creates a singleton instance of a given class when a property within it is accessed, not before. + +:::tip[Credits] +Credits to [Poimandres](https://pmnd.rs/) for the original code in [R3F Rapier](https://github.com/pmndrs/react-three-rapier) +::: + +## Usage + +```ts +import { createSingletonProxy } from 'ngxtension/singleton-proxy'; + +const { proxy: worldProxy, reset: resetWorld } = createSingletonProxy(() => new rapier.World([0, -9.81, 0])); + +worldProxy.gravity; // rapier.World() won't be created until this point + +resetWorld(); // reset the instance +``` diff --git a/libs/ngxtension/singleton-proxy/README.md b/libs/ngxtension/singleton-proxy/README.md new file mode 100644 index 00000000..854c7198 --- /dev/null +++ b/libs/ngxtension/singleton-proxy/README.md @@ -0,0 +1,3 @@ +# ngxtension/singleton-proxy + +Secondary entry point of `ngxtension`. It can be used by importing from `ngxtension/singleton-proxy`. diff --git a/libs/ngxtension/singleton-proxy/ng-package.json b/libs/ngxtension/singleton-proxy/ng-package.json new file mode 100644 index 00000000..b3e53d69 --- /dev/null +++ b/libs/ngxtension/singleton-proxy/ng-package.json @@ -0,0 +1,5 @@ +{ + "lib": { + "entryFile": "src/index.ts" + } +} diff --git a/libs/ngxtension/singleton-proxy/project.json b/libs/ngxtension/singleton-proxy/project.json new file mode 100644 index 00000000..0dd9a502 --- /dev/null +++ b/libs/ngxtension/singleton-proxy/project.json @@ -0,0 +1,33 @@ +{ + "name": "ngxtension/singleton-proxy", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "library", + "sourceRoot": "libs/ngxtension/singleton-proxy/src", + "targets": { + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "libs/ngxtension/jest.config.ts", + "testPathPattern": ["singleton-proxy"], + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "codeCoverage": true + } + } + }, + "lint": { + "executor": "@nx/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": [ + "libs/ngxtension/singleton-proxy/**/*.ts", + "libs/ngxtension/singleton-proxy/**/*.html" + ] + } + } + } +} diff --git a/libs/ngxtension/singleton-proxy/src/index.ts b/libs/ngxtension/singleton-proxy/src/index.ts new file mode 100644 index 00000000..c1b7759c --- /dev/null +++ b/libs/ngxtension/singleton-proxy/src/index.ts @@ -0,0 +1 @@ +export * from './singleton-proxy'; diff --git a/libs/ngxtension/singleton-proxy/src/singleton-proxy.ts b/libs/ngxtension/singleton-proxy/src/singleton-proxy.ts new file mode 100644 index 00000000..aa65d007 --- /dev/null +++ b/libs/ngxtension/singleton-proxy/src/singleton-proxy.ts @@ -0,0 +1,47 @@ +/** + * Original code by PMNDRS + * Source: https://github.com/pmndrs/react-three-rapier + * License: MIT License (or specify the appropriate license) + * + * Creates a proxy that will create a singleton instance of the given class + * when a property is accessed, and not before. + * + * @returns A proxy and a reset function, so that the instance can created again + */ +export function createSingletonProxy< + SingletonClass extends object, + CreationFn extends () => SingletonClass = () => SingletonClass +>( + /** + * A function that returns a new instance of the class + */ + createInstance: CreationFn +): { proxy: SingletonClass; reset: () => void } { + let instance: SingletonClass | undefined; + + const handler: ProxyHandler = { + get(_, prop) { + if (!instance) { + instance = createInstance(); + } + return Reflect.get(instance!, prop); + }, + set(_, prop, value) { + if (!instance) { + instance = createInstance(); + } + return Reflect.set(instance!, prop, value); + }, + }; + + const proxy = new Proxy({} as SingletonClass, handler) as SingletonClass; + + const reset = () => { + instance = undefined; + }; + + /** + * Return the proxy and a reset function + */ + return { proxy, reset }; +} diff --git a/tsconfig.base.json b/tsconfig.base.json index b7bfe01a..6dade668 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -44,6 +44,9 @@ ], "ngxtension/repeat": ["libs/ngxtension/repeat/src/index.ts"], "ngxtension/resize": ["libs/ngxtension/resize/src/index.ts"], + "ngxtension/singleton-proxy": [ + "libs/ngxtension/singleton-proxy/src/index.ts" + ], "ngxtension/trackby-id-prop": [ "libs/ngxtension/trackby-id-prop/src/index.ts" ],